Advanced Graphics and Data Visualization in R
Lecture 04: Expressing expression data
0.1.0 An overview of Advanced Graphics and Data Visualization in
R
“Advanced Graphics and Data Visualization in R” is
brought to you by the Centre for the Analysis of Genome Evolution &
Function’s (CAGEF) bioinformatics training initiative. This CSB1021 was
developed to enhance the skills of students with basic backgrounds in R
by focusing on available philosophies, methods, and packages for
plotting scientific data. While the datasets and examples used in this
course will be centred on SARS-CoV-2 epidemiological and genomic data,
the lessons learned herein will be broadly applicable.
This lesson is the fourth in a 6-part series. The aim for the end of
this series is for students to recognize how to import, format, and
display data based on their intended message and audience. The format
and style of these visualizations will help to identify and convey the
key message(s) from their experimental data.
The structure of the class is a code-along style in
R markdown notebooks. At the start of each lecture, skeleton versions of
the lecture will be provided for use on the University of Toronto datatools
Hub so students can program along with the instructor.
0.2.0 Lecture objectives
This week will focus on standard visualizations of expression data.
While our prior topics have focused on making visualizations that are
applicable broadly to many types of data, a number of this week’s
visualizations are used mainly on multi-dimensional data from
experiments like RNAseq.
At the end of this lecture you will have covered the following
topics
- Visualizing flowcharts and workflows
- Scatterplot variants of RNAseq data
- Colour gradient visualizations aka heatmaps
- RNAseq data analysis with
goseq
- Visualizing relationships between samples
0.3.0 A legend for text format in R markdown
grey background - a package, function, code, command or
directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or
folder
bold - heading or a term that is being defined
blue text - named or unnamed
hyperlink
... - Within each coding cell this will indicate an area
of code that students will need to complete for the code cell to run
correctly.
Blue box: A key concept that is being introduced
Yellow box: Risk or caution
Green boxes: Recommended reads and resources to
learn Python
Red boxes: A comprehension question which may or may
not involve a coding cell. You usually find these at the end of a
section.
0.4.0 Lecture and data files used in this course
0.4.1 Weekly Lecture and skeleton files
Each week, new lesson files will appear within your RStudio folders.
We are pulling from a GitHub repository using this Repository
git-pull link. Simply click on the link and it will take you to the
University of Toronto datatools
Hub. You will need to use your UTORid credentials to complete the
login process. From there you will find each week’s lecture files in the
directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find
a partially coded skeleton.Rmd file as well as all of the
data files necessary to run the week’s lecture.
Alternatively, you can download the R-Markdown Notebook
(.Rmd) and data files from the RStudio server to your
personal computer if you would like to run independently of the Toronto
tools.
0.4.2 Live-coding HTML page
A live lecture version will be available at camok.github.io
that will update as the lecture progresses. Be sure to refresh to take a
look if you get lost!
0.4.3 Post-lecture PDFs
As mentioned above, at the end of each lecture there will be a
completed version of the lecture code released as a PDF file under the
Modules section of Quercus.
0.4.4 Data used in this lesson
Today’s datasets will focus on differential expression analysis. The
basic visualizations of this data after it has already been processed by
packages like DESeq2.
0.4.4.1 Dataset 1: Lecture04_sankey_data.csv
This is an example dataset used for building a Sankey diagram. It
builds a theoretical workflow for RNAseq analysis and visualization
within a manuscript. What is a Sankey diagram? We’ll find out!
0.4.4.2 Dataset 2: Wyler2020_AEC_SARSCoV2_17AAG_readcounts.tsv
RNA-Seq read count data generated from SARS-CoV2 infections of AEC
cells. Used to compare the timecourse of expression (pro-inflammatory)
changes in samples treated with and without HSP90 inhibitors. Published
in iScience doi: https://doi.org/10.1016/j.isci.2021.102151
0.4.4.3 Dataset 3: Blanco-Melo2020Cell.Supp1.xlsx
This is an RNAseq dataset comparison for infection of multiple cell
types with pathogens like SARS-CoV-2, SARS-CoV-1, MERS-CoV, RSV
(respiratory syncytial virus), IAV (influenza A virus) and HPIV3 (human
parainfluenze virus type 3) published in Cell doi:
10.1016/j.cell.2020.04.026
0.4.4.4 Dataset 4: Blanco-Melo2020_Supp_Data_4.xlsx
This is an RNAseq dataset comparison for infection of human alveolar
adenocarcinoma (A549) cells with and without influenza A virus (IAV)
published on bioRxiv doi: https://doi.org/10.1101/2020.03.24.004655
0.4.4.5 Dataset 5: Lecture04.RData
A saved file with a final GO annotation dataset we look at with our
visualizations of dot plots.
0.5.0 Packages used in this lesson
tidyverse which has a number of packages including
dplyr, tidyr, stringr,
forcats and ggplot2
viridis helps to create color-blind palettes for our
data visualizations
networkD3, gghighlight,
ggrepel, and UpSetR are used in building some
of our visualizations including flowcharts, and upset plots.
goseq, and org.Hs.eg.db are packages that
will help us perform some basic Gene Ontology (GO) annotations with our
data.
# # Install these packages onto r.datatools
# install.packages("devtools")
# install.packages("networkD3")
# install.packages("gghighlight")
# install.packages("ComplexUpset", type="source")
# install.packages("GGally")
# install.packages("ggrepel")
#
#
# # 220331: Current version of JupyterHub is not allowing goseq to be installed correctly
#
# # # Some packages can be installed via Bioconductor
# if (!requireNamespace("BiocManager", quietly = TRUE))
# install.packages("BiocManager")
# BiocManager::install(version = "3.18")
#
# # This should install goseq 1.54.0. Check this under the packages tab!
# BiocManager::install("goseq")
# BiocManager::install("org.Hs.eg.db")
#
# # This last part will install some older version packages so that ComplexUpset will run properly
# devtools::install_version("dplyr", version = "1.1.3", repos = "http://cran.us.r-project.org")
# devtools::install_version("ggplot2", version = "3.4.4", repos = "http://cran.us.r-project.org")
We’re playing fast and loose with some installations here to get it
to work on r.datatools.utoronto.ca so after the installation is
complete, restart your session with Session > Restart R.
Don’t refresh your browser or you’ll lose the installation.
# Packages to help tidy our data
library(tidyverse)
library(readxl)
# Packages for the graphical analysis section
library(viridis)
# Lecture 04 visualization packages
library(networkD3)
library(GGally)
library(gghighlight)
library(ggrepel)
library(ComplexUpset)
Load these libraries separately to save on a little bit of memory
usage. We’ll need every last drop we can get.
# GO term analysis packages if you managed to install goseq
library(goseq)
library(org.Hs.eg.db)
1.0.0 Working with large amounts of data
Until this point, the data sets we have used in lecture have been
relatively simple epidemiological information. All of our observations
are values like new cases or vaccinated individuals tied to dates or
time periods. Whereas most of our observations were connected in some
linear fashion, the data we examine today will have tens of thousands of
observations, each representing a different gene!
Given the complexity of our data, we will discuss ways to sort,
partition, visualize and interpret it.
1.1.0 Where does RNAseq data come from?
Many of you may be familiar with the idea of RNAseq data. It begins
in the real world (or at the bench) with individuals/groups/conditions
compared against a control state. Biological samples can be collected in
time series or at a single endpoint. Each experimental condition should
have it’s matching control or baseline profile for comparison, not to
mention biological replicates!
After collecting your samples you still need to prepare and sequence
your libraries, check your data quality, trim reads, map them to a
transcriptome, estimate/normalize the expression of genes within each
library and then compare the expression between
libraries/samples to determine if there are transcriptional differences!
All of these steps are beyond this class BUT if you plan on working with
transcriptomes, you’ll eventually encounter this process. Today we’ll
jump the line and work directly with differential expression (DE) data.
That means we’ll be at the bottom right corner of the following diagram
AKA, the end of the pipeline.
1.2.0 Use a Sankey diagram to illustrate your workflow
Sankey diagrams are named after Irish Captain Matthew Henry Phineas
Riall Sankey, who first presented this type of visualization in 1898 to
convey the energy efficiency of a steam engine!
With larger sets of data across multiple experiments, it can
sometimes be helpful to use a Sankey diagram to explain the connection
between different aspects of your samples. The key attribute of the
Sankey diagram is that the width of a connection (flow) is proportional
to the quantity represented. So the Sankey diagram is a flow diagram
that also visually quantifies the dynamics of the relationships in your
diagram. Think of it much like a stacked bar chart that can split or
join with other bars at each connection.
A useful package to generate Sankey diagrams is
networkD3 using the sankeyNetwork() function.
To generate a Sankey diagram, you need information on two variables with
a third optional one:
source: a starting point or for your flow
target: the end point of your flow
value: the number that will represent the width of your
flow. Usually some quantity of contribution but this can also be an
optional variable.

We’ll take the time to reconstruct this sankey plot in the following
sections.
We’ll find a pre-made table that describes a theoretical RNAseq
workflow based in the datasets we’ll be working with today. Some
sections have been compressed for brevity but we’ll find all of that
information in ./data/Lecture04_sankey_data.csv. Let’s
begin by reading it in.
# Read in our Sankey diagram in ./data/Lecture04_sankey_data.csv
sankey_info.df <- read_csv(...)
# Check the structure
str(sankey_info.df)
# Look a few rows
head(sankey_info.df)
From the above output we can see that one source may
have multiple target values and vice versa.
1.2.1 Convert your flow data frame into a node data frame
Although our data frame sankey_info.df contains the
information about our connections, the networkD3 package
requires some mapping of the nodes within our data. Each unique source
and target will be counted as a node and we’ll save that information
into sankey_nodes.df.
# Combine our source and target lists, then take the unique set and put that into the "name" vector.
# sankey_nodes.df <- data.frame(name=c(as.character(sankey_info.df$source), # Location of our source nodes
# as.character(sankey_info.df$target)) %>% # Location of our target nodes
# unique() # Generate the unique combination of the two sets
# )
# Combine our target and source nodes into a single vector
sankey_nodes.df <- c(as.character(...),
as.character(...)) %>%
# Only keep the unique values
unique() %>%
# Put this into a data frame under a column called "name"
data.frame(name = .)
# Look at the structure, it's just a list of the nodes
str(sankey_nodes.df)
1.2.2 Map your node names back to your source and target data with
match()
Now that we have our node information (18 total unique nodes) stored
basically as an identification number, we have to map it back to
sankey_info.df. This is essentially like working with a
factor that spans two columns instead of just one. To accomplish the
mapping, we’ll use the match() method. The
match() function takes two values:
x: the values to be matched
table: the values to be matched against
and returns a vector of length x where each integer
value represents the position of its matched value from
table. Unmatched values from x (ie not found
in table) are set to nomatch.
match() vs “%in%”: The match()
function and the matching operator %in% are very
similar in idea. Whereas the former produced a set of matched positions
between sets, the latter produces a logical vector indicating inclusion
(TRUE) or exclusion (FALSE) from the intersect of the sets.
Let’s try with a quick example with our data
# Which nodes match from our source nodes to our collected nodes (18 total nodes)?
match(sankey_info.df$..., sankey_nodes.df$...)
Note that our node information must be 0-indexed! R uses an indexing
scheme that begins at 1 so we’ll need to offset that information that we
generate as well.
# Add our sankey_nodes.df as index information that maps between it and `sankey_info.df`
sankey_info.df <-
sankey_info.df %>% mutate(IDsource = ...,
IDtarget = ...) # nodes must be 0-indexed
# take a peek at what we've wrought
head(sankey_info.df)
1.2.3 Build your Sankey diagram with
sankeyNetwork()
Now that we have our two necessary data frames, we can build our
Sankey diagram with the sankeyNetwork() function. This
function has a few parameters that we’ll want to set:
Links: the location of our source/target
relationship information (sankey_info.df).
Nodes: the listing of nodes in our diagram
(sankey_nodes.df).
Source: the name of the source column
(IDsource).
Target: the name of our target column
(IDtarget).
Value: the name of our “optional” value to build the
width of flows (value).
NodeID: the names of our nodes
(sankey_nodes.df$name).
There are an additional number of parameters that allow you to play
with the visualization of this diagram including:
colourScale: categorical colour of your node. Works
together with…
NodeGroup: for colouring your nodes based on an
extra column in Nodes.
LinkGroup: for colouring your links based on an
extra column in Links.
nodeWidth: the numeric width of each node.
For more information on these parameters and others, you can go here
# Build the Sankey diagram with all of our information
sankeyNetwork(Links = ..., # Where is the "edge" information
Nodes = ..., # Where is the "node" information
Source = ..., # Where are the source node labels
Target = ..., # Where are the target node labels
Value = ..., # What value do we assign to the relationships/edges
NodeID = ..., # Where are the labels for the nodes stored?
sinksRight = TRUE, # Push the nodes to the right
fontSize = 10)
1.3.0 Scatterplot matrices for some quick QA of your read
counts
Stepping back one step in our flowchart, just prior to your DE
analysis, you’ll have a set of read counts generated from RNA-Seq data.
The counts are an estimation of each transcript within your data. Some
programs may include the analysis of spliced isoforms and others may
not.
Often in your RNA-Seq datasets, you should have multiple replicates
of your data. A quick way to assess the quality of your datasets is to
generate a scatterplot matrix. The scatterplot matrix is an all-by-all
assessment of your experiments that generates a scatterplot of read
counts for each pair-wise dataset.

A scatterplot matrix can help to quickly assess the quality and
consistency of your data.
Figure taken from: Visualization methods for differential expression
analysis. Rutter et al., 2019. BMC Bioinformatics 20: 458
1.3.2 Prepare your data as a clean read count matrix
Before we proceed to generating our scatterplot matrix, we need to
select our data from our main dataset. In particular, the data from
Wyler et al., that we imported as
wyler_readcounts.df contains data from infecting human
airway epithelial cells (AECs) and treating with either
DMSO or 200nM of the HSP90 inhibitor,
17-AAG. With each condition there is a set of infected
(S2) or uninfected AECs. For each of these 4
conditions, there are 3 replicates and 3 timepoints
(24-, 48-, and
72-hours). In total that amounts to 36 sets of
data.
For our purposes, we’ll:
Compare 24h vs 72h infected (ie S2 = SARS-CoV2) DMSO-treated
found in columns 9-11 and 33-35. We can either select by index or use a
selection helper like matches() to incorporate a regular
expression.
Filter our reads to the range of 10-5000 using the
if_all() function.
3. Rename our columns to remove some redunant data
(AECII_xx_)
Note that the if_all() function is a rather new addition
to the tidyverse and is used to help filter on a predicate across
multiple columns. This is very much like the
across() helper function you may have used previously for
summarizing data. Like the across() function, the
if_all() helper function uses the following parameters:
You’ll notice our use of ~ again to anonymize the
function.
readcounts_24hv72h <-
wyler_readcounts.df %>%
# Select for just the columns withs SARS-CoV-2 infection and DMSO at 24 and 72h
dplyr::select(...) %>%
# Restrict our data analysis to just readcounts above 10 and below 5000
filter(if_all(.cols = ..., .fns = ~ .x >10 & .x < ...)) %>%
# Rename the columns by removing the first portion: AECII_xx
rename_with(., ~ str_replace(string = .x,
pattern = r"(\w*_\d*_)",
replace = ""))
# Take a peek at our resulting tibble
str(readcounts_24hv72h)
1.3.3 Use ggpairs() to generate a scatterplot
matrix
You may recall our working with the parallel coordinate plot from
lecture 2. Rather than transform the data ourselves, we
passed our data along to the ggparcoord() function from the
GGally package. Well that package is here to simplify our
lives again! Rather than generating the paired data between each
experiment ourselves, we can pass along the read count matrix we just
generated to the ggpairs() function. Parameters of the
ggpairs() function that we’ll use include:
data: the dataset containing our data. It can include
both numerical and categorical data.
mapping: the aesthetic mapping (aside from
x and y) used for altering your format.
columns: which columns from data are used
to generate the plot. Defaults to all columns.
title, xlab, ylab: the titles
of your various graph components.
upper and lower: named lists that may
contain the variables “continuous”, “combo”, “discrete”, and “na” used
to determine how pairwise combinations of continuous and/or categorical
data are plotted across the grid. You basically need to choose how these
data combinations will be plotted.
diag: a named list that may only contain the variables
“continuous”, “discrete”, and “na”. Each of which is set to a specific
kind of plot type (ie “densityDiag”, “barDiag”, or “blankDiag”).
Moreover, you may recall that GGally works with the
ggplot or is ggplot-extensible so we
can use many of the same features from ggplot to fix some of the
aesthetics of the result object. In the end, you’ll notice that this is
basically a faceted scatterplot but the dataframe required to generate
it is more complex than what we currently have. For more information on
this function, you can check out the GGally manual
or check out the list of
great examples provided on the authors’ github.
# Create your matrix scatterplot with ggpairs
ggpairs(readcounts_24hv72h,
# Set the alpha of our points so we can see them better
mapping = ...,
# We'll play with the correlation text so the values are little larger than default for us
upper = list(continuous = wrap("cor", size = 9))
) + # Set the alpha of our points so we can see them better
# Change some of the theme aspects like text size
theme(text = element_text(size = 20),
# Rotate the x-axis tick text to be
axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
Work smarter, not harder: When it comes to certain
complex visualizations, it is best to avoid re-inventing the wheel.
There are so many packages available that accomplish a great number of
different visualizations. The key is to knowing what kind of
visualization you want to produce, and then checking if a package
exists. While the ggplot package can accomplish quite a
few basic and complexly layered visualizations, ones like the above
scatterplot matrix require the generation and replacement of multiple
plot types. This requires going to a deeper level of knowledge beyond
this course. A nice package like GGally takes away
those issues while offering the ability to still customize and alter
your plot format to a certain extent.
1.3.4 Interpreting a scatterplot matrix
As you can see, the scatterplot matrix can provide a quick way to
check that your replicates are similar, while your comparison conditions
should show more variation between them. Looking at our above plot, the
triangular sections of scatterplots in the top-left and lower-right
represent the comparison of replicates in our experiment. These should
produce scatter points that are close to the diagonal - meaning the
replicates are quite similar in the data produced.
In the lower-left corner of the plot we find the comparison between
our two conditions (24- vs 72-hour data) and you can see much more
scatter away from the diagonal. If our conditions really do induce
different transcriptional profiles, this is what we should expected to
see.
Across the diagonal we see the density plot of our read counts. These
aren’t particularly helpful with this dataset as the data predominantly
appears to have low read counts even after we filter for a minimum of 10
reads per gene.
This technique is also a useful way to identify potentially
mislabeled samples before processing your data for analysis. Changes
from the above pattern can signal the mislabeling of samples or changes
to the process of sample preparation.
Not just for expression analysis! While we have use
the scatterplot matrices in this section to compare our RNAseq datasets,
these plot formats have a much broader use in determining if any
possible linear correlation may exist between continuous
variables. It’s a quick way to look at a wide scope of variables
for possible relationships without doing all the in-depth analysis
first. Think of it as yet another helpful tool for exploratory data
analysis!
1.4.0 What does differential expression data look like?
Digging down into your DE results, with the human genome, you are
looking to identify trends in gene expression differences across 43,000
potential transcripts sequences - only half of which produce proteins.
You can imagine that opening up a tabular file of that size could take a
bit of time.
For each DE experiment there are a number of values generated. In a
popular package like DESeq2 you will typically find:
| Gene name |
The names of your genes which may be in different
formats like Entrez ID, Ensembl, or gene symbol |
| Base mean |
Average of the normalized counts values, accounting for
size factors, taken over all samples (and or replicates) |
| Log\(_{2}\)
fold-change |
The effect size estimate - how much your gene’s
expression has changed from control/base conditions |
| Log fold-change SE |
An estimate of effect size uncertainty |
| p-value |
Hypothesis test against H\(_{0}\) that no difference exists between
sample groups |
| adjusted p-value |
An adjusted p-value after taking into account multiple
testing between groups/samples |
You can find more information on working with the DESeq2
package here
1.5.0 You can examine data on many scales
Once we have a set of data we can examine it from many aspects:
- Compare all genes within a sample and across samples
- Compare DE in a subset of genes within a sample and across
samples
- Compare DE results based on gene function
Let’s explore these different aspects and the kind of plots we can
generate to accomodate the size of our data set. First, however, we need
to find a useful data set to work with.
1.5.1 Use the readxl library to open your .xlsx
files
We’ve briefly touched on using this package in
Lecture 01 to help read our Microsoft Excel-based data.
Today’s data set from Blanc-Melo et al. (Cell 2020), comes to us in the
form of an excel file. Let’s use some of the tools from this package to
help open up our data file. We’ll start by peeking at how many sheets
there are using excel_sheets().
# What are the sheets to open in our data set?
excel_sheets(...)
1.5.2 Open each sheet separately with read_excel()
Now that we can see there are two sheets in our data, we can assign
each one to a different data frame using the read_excel()
command which contains the parameter sheet. We can use this
to determine which sheet to load either based on it’s position (integer)
or it’s name as specified by excel_sheets().
# Assign our legend
blanco_legend.df <- read_excel("./data/Blanco-Melo2020Cell.Supp1.xlsx", sheet=...)
# Assign our data
blanco_data.df <- read_excel("./data/Blanco-Melo2020Cell.Supp1.xlsx", sheet=...)
str(blanco_legend.df)
str(blanco_data.df)
1.5.3 ABW: Always Be Wrangling (your data)
Yes, it looks like the legend has some helpful information about the
dataset itself found in the second sheet. The DE data looks like it’s
split across 20 columns encompassing 10 experimental data sets. For each
data set, it looks like there is a Log\(_{2}\) fold-change value
(L2FC) and an adjusted p-value
(padj).
Let’s take a closer look at blanco_legend.df first. From
it we can parse out some important information about the experiments
themselves like the pathogen and host
in each experimental set.
# Look at the entire legend
blanco_legend.df
1.5.4 Extract substring patterns and save them using
str_match_all()
You may remember that we can easily match for patterns in our string
using functions from the stringr package but with a
function like str_match_all() you can also include matching
groups to your pattern that can be captured. For each
string match, this function will return a matrix that contains the
complete matching pattern and any grouped patterns that are denoted by
the () parentheses.
If a string contains multiple matches to your pattern, it
will generate additional rows in the matrix for that string.
Looking at our Notes column of
blanco_legend.df, it looks like it follows a regular
pattern were we can extract the pathogen and host cell information
| Pathogen name |
SARS-CoV-2, RSV |
| Intermediate strings |
infection, infection with Ruxolitinib |
| Host cell type |
A549, A549-ACE2 |
# pattern match for pathogen and host information and save it into a datafram you can access later.
exp_info <-
blanco_legend.df %>%
# Use dplyr::select because the select function has been overridden by another library
dplyr::select(Notes) %>%
# Use str_match_all to grab the pattern we want, piece by piece
# example string: SARS-CoV-2 infection (A549-ACE2 cells, MOI: 2, 24hpi)
str_match_all(pattern = r"(...)") %>%
# Set into a data frame
as.data.frame() %>%
# Change the column names (would be X1, X2, X3 otherwise)
`...`(c("notes", "pathogen", "host"))
# Alternative code to set names:
# magrittr::set_colnames(c("notes", "pathogen", "host"))
# Take a look at the resulting data
exp_info
# Add this new information to our original legend information
blanco_exp_info.df <-
blanco_legend.df %>%
# Make some new columns with pathogen and host information
mutate(pathogen = exp_info$pathogen, # Our pathogen information
host = exp_info$host) %>% # Our host information
# Combine our pathogen and host information into a single column as well
unite(col = ..., ..., sep="_", remove = FALSE) %>%
# Just keep the original Field column and the new ones
dplyr::select(1, 4, 5, 6)
# Take a look at what we are working with
blanco_exp_info.df
2.0.0 Visual inspection of DE across a sample
After all of our data wrangling, you can see we have a data frame
with nearly 1/4 million observations. This spans across 10 experimental
conditions. From a broad level, we can look across entire data sets to
identify genes of interest. There are a couple of ways to do this and
we’ll begin with a standard set of plots that can convey the overall
distribution of our data. While our view of the data is low-resolution
we are still able to compare specific gene information centered around
mean expression levels, magnitude of comparison, and statistical
significance.
2.1.0 The MA plot describes the spread of our DE data
Used originally in the application of Microarray data analysis, these
plots are also applied to visualizing high-throughput sequencing
analysis.
This is a data transformation between two sets of data such that
We plot our M values along the y-axis against the
corresponding A values on the x-axis. Do these
characteristics sound familiar? They are standard output for
DESeq2 data sets! Unfortunately our above data doesn’t have
that information anymore, but we have a dataset that does! It’s another
dataset from an earlier pre-publication copy of the Blanco-Melo et al.,
2020 paper: Blanco-Melo2020_Supp_Data_4.xlsx.
Let’s open this up and take a look!
# We'll be looking at DE data for IAV infection of A549 cells
DE_A549_IAV.df <- read_excel(..., sheet=2)
# Look at the data frame we've made
head(DE_A549_IAV.df)
2.1.1 ABmW: A Bit more Wrangling with across()
Okay, so we can’t use the data right away. The
read_excel() command has decided to convert some of our
columns to character format because it detected NaN values.
So we’ll have to quickly coerce the data before moving on. We can
quickly convert the data with a mutate() command using the
helper verb across().
To successfully use this helper, we will want to list out the columns
we want to use in the .cols parameter, and then apply a
function like as.numeric. We don’t actually need
all of these columns, but it’s good practice!
# Convert our numeric columns to the proper type.
DE_A549_IAV.df <-
DE_A549_IAV.df %>%
# Use mutate() with across() to convert columns to numeric
mutate(...)
# Check the result
str(DE_A549_IAV.df)
Skip the wrangling with a proper import! While we
used the above import as an opportunity to practice our wrangling
skills, we could have avoided it altogether if we imported properly.
Within the read_excel() function there is a parameter
called “na” which allows us to define the character(s)
that represent null or NA values. The default is simply blank or empty
cells but knowing what we do now about the dataframe, we could also have
used set the parameter: na = “NaN” which would allow
our dataframe to properly import the values and correctly assign
variable types. So, it’s always helpful to know
what your functions can do!
2.1.2 Build your MA plot using ggplot2
Given that we already have our data in a differential expression
format, we can just use ggplot2 to plot the correct
variables to the x and y axes. In this case, we are interested in using
the log2FoldChange as our M and
baseMean represents our A. There are a number
of ways we can colour this plot but we’ll take into account our
padj values to highlight those genes with DE values that
are statistically significant.
Now, if we were using raw or normalized expression data, we would
need to calculate the proper values for the transformation of
M and A. There are a few packages that can
take care of this for you such as ggmaplot which you can
find here
For our plot, to differentiate between our points, let’s use the
gghighlight package from last week! What will our
predicates be based on? Log2 fold change? Adjusted
p-value?
# MA plot! Need to select based on a fold change > x and padj <= fdr
# Usually FC = 1.5, fdr = 0.05 but we'll use the "status" variable here instead.
DE_A549_IAV.df %>%
filter(status == "OK") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=baseMean, y = log2FoldChange) +
# Theme
theme_bw() +
theme(text = element_text(size=20)) +
# 3. Scaling
# Use a log10 scale on the x axis
scale_x_log10() +
# Set the breaks on our y-axis
scale_y_continuous(limits = c(-10, 10), breaks = seq(-10, 10, 2)) +
# 4. Geoms
# Colour our points all in red
geom_point(colour = "red", alpha = 0.2, size=4, na.rm=TRUE) +
### 2.1.2
# Change it so that only high L2FC with low p-values will be in red, the rest as black
gghighlight(..., ..., unhighlighted_params = list(colour = "black"))
2.1.3 Interpreting our MA plot
So our plot shows us the general distribution of the DE data. Looking
at it, we can see, notably, that it appears that we have many more genes
upregulated than downregulated in comparison to our control. We also
see, as expected, more low-probability variation occurring with lower
normalized mean counts. Conversely, as our overall mean counts increase,
we also see less variation in general but we do see a gene that does
pass threshold DE with a low enough p-value! To investigate further you
could filter the data or look again with a volcano
plot!
2.2.0 Volcano plots examine fold-change vs p-value
While the MA plots explored data based on its fold-change values in
relation to mean expression, the volcano plot looks purely at
differential expression based on it’s p-value (ie the
probability that the DE is real). To emphasize the importance of the
p-value we will -log transform it so smaller p-values
are higher up on the y-axis. This quickly partitions data points in the
visualization to draw high-DE/low p-value combinations away from the
bulk of the population.
To plot this data, we’ll return to our larger
blanco_data_long.df which contains data from multiple
experiments. For now, let’s focus on just the SARS-CoV-2 data that has
been generated in the A549 host cells. Our -log transformation
of the p-values will also be problematic when encountering 0, so be
careful! Let’s compare plots with and without these problem values.
To label our points of interest, we’ll also use the
geom_text_repel() from the ggrepel package.
This will help arrange and plot text onto our scatterplot in a way that
avoids collisions with the data and the other labels! You could also
experiment with using the directlabels package that we
played around with last week to accomplish a similar feat.
# Remind ourselves what our data table looks like
head(blanco_data_long.df)
# Remove p-values = 0
volcano.plot1 <-
# Volcano plot!
blanco_data_long.df %>%
filter(pathogen == "SARS-CoV-2", host=="A549", padj !=0) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=L2FC, y = -log10(padj)) +
# Themes
theme_bw()+
theme(text = element_text(size=20)) +
labs(title = "Volcano plot with p-values == 0 removed") +
# 4. Geoms
# points all coloured red
geom_point(colour = "red", alpha = 0.7, size=4) +
# highlight leaves only the ones meeting our criteria as red, colour rest as blue
gghighlight(abs(L2FC) > 2, padj < 0.05, unhighlighted_params = list(colour = "lightblue")) +
### 2.2.0
# Add labels but only for the top 10 genes
geom_text_repel(data = blanco_data_long.df %>%
# Filter the data used to label
filter(... == "SARS-CoV-2", ...=="A549", abs(L2FC) > 2, padj !=0) %>%
# Arrange in ascending order
arrange(padj) %>%
# Take the first 10 results
dplyr::slice(1:10),
aes(label = ...), size=6)
# leave in p-values = 0
volcano.plot2 <-
# Volcano plot!
blanco_data_long.df %>%
filter(pathogen == "SARS-CoV-2", host=="A549") %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
aes(x=L2FC, y = -log10(padj)) +
# Themes
theme_bw()+
theme(text = element_text(size=20)) +
labs(title = "Volcano plot with all p-values") +
# 4. Geoms
# points all coloured red
geom_point(colour = "red", alpha = 0.7, size=4) +
# highlight leaves only the ones meeting our criteria as red, colour rest as blue
gghighlight(abs(L2FC) > 2, padj < 0.05, unhighlighted_params = list(colour = "lightblue")) +
# Add labels but only for the top 10 genes
geom_text_repel(data = blanco_data_long.df %>%
# Filter the data used to label
filter(pathogen == "SARS-CoV-2", host=="A549", abs(L2FC) > 2) %>%
# Arrange in ascending order
arrange(padj) %>%
# Take the first 10 results
dplyr::slice(1:10),
aes(label = GeneName), size=6)
# Plot both of our figures together
fig.show="hold"; out.width="50%"
volcano.plot1
volcano.plot2
2.2.1 Interpreting your volcano plots
Looking at our volcano plots, we can clearly see the volcano shape we
are looking for. The data is split between our \(\pm\) log\(_{2}\) fold changes with points highlighted
when we see DE beyond this. You can customize your parameters but you
can also see the emphasis on lower p-values in our data set. The most
“significant” data is found in the upper left and right quadrants of the
graph, which wasn’t made clear in the MA plot. You could use the same
tricks to label your MA plot too but the data could end up anywhere
along the MA plot.
In the end, if you do have access to the original counts, it
may be of use to consider this information when further investigating
your candidate genes from DE.
Section 2.0.0 Comprehension Question: Comparing our
two versions of the volcano plot, we see different sets of genes
highlighted as the “top 10” based on adjusted p-values. What makes these
two sets of data different? Why do we see datapoints at the top edge of
our plots in the second version?
3.0.0 Medium resolution analysis focuses on groups of genes
Now that we’ve taken an overall look at our data, we can begin to
assess our data based on some candidate genes or hypothesis testing. For
instance, you may only be interested in looking at genes with an L2FC
> 3. In other cases, you may be interested in a group of genes
pertaining to a function like the chemokines which are part of the
inflammation response.
After selecting a subset of genes we can begin to compare their DE
data more meaningfully across different sample sets. Let’s continue
working with our long-format dataset blanco_data_long.df
which contains our multiple RNAseq experiments from various infection
conditions. We will begin by generating a list of the highest DE genes
with low p-values in the SARS-CoV-2 infection scenario.
Note here we’ll use the pull() function which is a
nicer-looking way to retrieve a single column as a vector from
our tibble object. This is equivalent to using a
.$colName format.
# Select the genes we want to visualize
heatmap_gene.list <-
blanco_data_long.df %>%
# filter for data from a specific experiment, and then by high positive L2FC values with low p-values
filter(pathogen == "SARS-CoV-2", host == "A549", L2FC > ..., padj < ...) %>%
...
# Take a look at the resulting list
heatmap_gene.list
3.1.0 Use heatmaps to visualize data across a continuous range of
values
Now that we have generated a select group of genes to examine, our DE
data is well-suited to visualizing in heatmap or heat plot. In our
heatplot we can generate values along two categorical axes. On the
y-axis we can plot DE values from our individual genes, while on the
x-axis we will show values across multiple experiments. To visualize the
data itself, we will use the geom_tile() command.
# heatmap of our data based on a list of top DE genes from the SARS-CoV-2 infection of A549 cells
blanco_data_long.df %>%
filter(GeneName %in% heatmap_gene.list,
host == "A549"
) %>%
# mutate(GeneName = factor(GeneName, levels = heatmap_gene.list)) %>%
# 1. Data
ggplot(.) +
#2. Aesthetics
### 3.1.0 set aesthetics
aes(x=..., y = ..., fill=...) +
# Theme
theme_bw()+
theme(text = element_text(size=18)
)+
labs(title = "Heatmap of L2FC values in A549 cells infected by different pathogens") +
# 3. Scaling
# Keep this to reverse the y axis or put it into the assignment?
scale_y_discrete(limits = rev) +
scale_fill_viridis_c(option="plasma") +
# Geoms
### 3.1.0 Use a tile and set the width a little smaller to give a gap between groups
geom_tile(...)
# heatmap of our data based on a list of top DE genes from the SARS-CoV-2 infection of A549 cells
# Split x-axis by experiment name
blanco_data_long.df %>%
filter(GeneName %in% heatmap_gene.list,
...
) %>%
# 1. Data
ggplot(.) +
#2. Aesthetics
aes(x=experiment, y = GeneName, fill=L2FC) +
# Theme
theme_bw()+
theme(text = element_text(size=18),
axis.text.x = element_text(angle=45, hjust = 1)
) +
labs(title = "Heatmap of L2FC values in SARS-CoV2 infections by experiment") +
# 3. Scaling
# Keep this to reverse the y axis or put it into the assignment?
scale_y_discrete(limits = rev) +
scale_fill_viridis_c(option="plasma") +
# Geoms
# Use a tile and set the width a little smaller to give a gap between groups
geom_tile(width = 0.95)
3.1.1 Interpreting a heatmap of upper values
From our list of the top 18 DE hits in our SARS-CoV-2 set, it looks
like we see some similar hits across some of the genes when infecting
A549 cells with other viruses like HPIV3 and RSV.
We see strong L2FC values for IL6, IL1A and LAMC2 for instance. IL6
(interleukin 6) has been reported to play a role in both mounting an
effective immune response to certain viral infections but its
interactions with other factors may also have a potential role in the
exacerbation of viral phenotypes. In looking across different host cell
infections by SARS-CoV-2 there is again consistent upregulation of IL1A
and IL6.
Using a similar method, we can instead focus on genes from a specific
family or pathway as long as we have a list. Let’s try the family of
chemokines which play a role in inducing cell movement during the immune
reponse.
# Build a list of the potential chemokines by looking for those matching the "CCL" pattern
heatmap_ccl.list <-
blanco_data_long.df %>%
# Filter specifically for any genes that have CCL in their name
filter(...) %>%
pull(GeneName) %>%
unique()
str(heatmap_ccl.list)
Now we can use this list in our ggplot visualization.
Note that we could have generated a much longer pipe series
without saving the object heatmap_ccl.list but then we
wouldn’t have a chance to examine it before plotting that data to learn
that it has 29 unique values.
# heatmap of our data based on a list of chemokine genes
blanco_data_long.df %>%
# Filter for genes in our list, and experiments where the host cell was "A549"
filter(GeneName %in% ...,
host == "A549"
) %>%
# 1. Data
ggplot(.) +
#2. Aesthetics
aes(x=experiment, y = GeneName, fill=L2FC) +
# Theme
theme_bw()+
theme(text = element_text(size=20),
axis.text.x = element_text(angle=45, hjust = 1)
)+
# 3. Scaling
scale_fill_viridis_c(option="plasma")+
# Geoms
# Use a tile and set the width a little smaller to give a gap between groups
geom_tile(width = 0.95)
3.1.2 Heatmap interpretation
From our heatmap, we can observe that looking only at the chemokines,
there is consistent upregulation of CCL5 and CCL2 when infecting our
A459 cells with any of these viruses. In comparison, however, HPIV3 and
RSV infection show a much higher DE across the CCL family in comparison
to SARS-CoV-2 which appears to illicit less inflammatory response in the
time-frame sampled.
But wait there’s more! While we are dealing with
just a subset of our RNA-Seq data, much more is available to visualize
on a greater scale. Next week we’ll dig deeper into high-dimensional
data and how heatmaps can be used to visualize and organize this kind of
information to help identify and convey patterns.
3.2.0 Looking at genes with dot plots
Note that in the above heatmap we no longer have any information on
the adjusted p-values from these L2FC scores. Something to keep in mind
when making these heatmaps. What would happen if we filtered on p-values
as well? Is there a way we could track that information?
If we replace our geom_tile() with a
geom_point() we are able to add an additional dimension to
our data by utilizing size mapping.
We’ll repeat our visualization from above but use colour to represent
the L2FC values and a size to represent the
padj values in our dataset.
# heatmap of our data based on a list of chemokine genes
blanco_data_long.df %>%
# Filter for genes in our list, and experiments where the host cell was "A549"
filter(GeneName %in% heatmap_ccl.list,
host == "A549"
) %>%
# 1. Data
ggplot(.) +
#2. Aesthetics
aes(x=experiment, y = GeneName) +
# Theme
theme_bw()+
theme(text = element_text(size=20),
axis.text.x = element_text(angle=45, hjust = 1)
)+
# 3. Scaling
scale_colour_viridis_c(option="viridis", ) +
scale_size_continuous(range = c(1, 10), breaks = c(0, 1.3, 10, 100)) +
# Geoms
### 3.2.0 Use colour and size in our dotplot to represent L2FC and padj
geom_point(aes(colour = ..., size = ...))
Now we can more clearly see which values are worth noting or
investigating. We can see that CCL2 has a more consistent
L2FC and padj combination although this would
require further investigation since our size range is still quite
limited. We can further zoom in on our data by specifically visualizing
our genes of interest!
3.2.1 Looking at single genes with dot plots
We can further narrow our search instead of looking at whole gene
groups, and pick a small set of specific genes. We’ll use a dotplot
to visualize the data where the y-axis uses the L2FC value to place
points and we’ll use a categorical x-axis of gene lists. We’ll filter
our data to only include infections in the A549 host, and also group our
data in a few ways:
- We’ll scale our point sizes based on the adjusted p-value to get a
better sense of how probable these changes are.
- We’ll use colour to define the pathogen used in each
experiment.
- We’ll also facet our data based on host.
single_genes = c("CCL5", "CCL2", "CCL20", "CCL17", "IL6", "IL1A")
blanco_data_long.df %>%
filter(GeneName %in% single_genes,
) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
### 3.2.0 set the aesthetics for our dot plot colouring by pathogen, shaping by host
aes(x=GeneName, y = L2FC, colour=..., shape=...) +
# Theme
theme_bw()+
theme(text = element_text(size=20)) +
# 3. Scaling
scale_colour_viridis_d(direction = -1,
guide = guide_legend(override.aes = list(size = 5))) +
# Scale the size range
scale_size(range = c(8, 4)) +
scale_shape(guide = guide_legend(override.aes = list(size = 5))) +
# 4. Geoms
### 3.2.0 adjust the dot size based on the adjusted p-value
geom_point(aes(size = ...), alpha = 0.8, stroke = 2) +
# 6. Facets
facet_wrap(~ host, ncol = 2, scales = "free_y")
3.2.1 Dot plot interpretation
Focusing on small sets of genes we can see some trends across our
groups. For instance, 3 of our pathogens produce a strong IL6 response
as we’ve seen in our heatmaps. We also see a >2 L2FC in CCL2 as a
response to infection by SARS-CoV-2. Actually our weakest IL6 response
to SARS-CoV-2 was seen in the ACE-2-expressing A459 hosts so our initial
heatmap in section 3.1.0 didn’t give us a full picture of what might be
happening.
It would be best to further investigate these on a more specific
subset of hosts and/or pathogens.
Comprehension Question 3.0.0 While we see some
inconsistency in our IL6/SARS-CoV-2 response in the above dotplot, are
we possibly missing some context? If you were to return to the source of
this data prior to our wrangling (see section 1.5.2),
you might note that there are 3 kinds of A549-ACE2 experimental groups.
What are some ways you could account for this information when
wrangling?
4.0.0 RNAseq analysis for significant functional terms
Up until this point, we’ve been feeling our way through the data,
rather… undirected. Often within manuscripts you will find
deeper analyses of differential expression by examining the collection
of functional terms to determine trends or pathways of interest. The
Gene Ontology (GO) resource represents a codification of gene function
through three domains: cellular component (CC), molecular function (MF),
and biological process (BP).
Most, if not all, of the genes in our dataset can be described in
some way by a GO term. Once we collect these terms, we can begin to look
for meaningful groups that could be considered over-represented (or
under-represented) in our dataset. Once we have collected this
information we can visualize it similar to our individual gene
dotplots.
4.1.0 Use the goseq package to pull down GO terms for
your genes
Using the goseq package we can perform a full GO term
analysis on our DE data with an analysis for enriched terms within our
set of over- or under-expressed genes. However, in order to begin the
process of pulling down GO terms, with the goseq package,
we also need to generate a named integer vector to split our gene sets
into the two relevant categories.
We’ll store our categorized set of genes in
genes_SARS_CoV_2 where a 1 represents over/underexpressed
genes in our set, and 0 represents all remaining genes.
# Set up the gene information we need for the GO-term analysis
# It needs to know which genes meet our FC criteria and which do not
#nrow(blanco_data.df)
# Casting our logical as an integer will create a 0/1 matrix
genes_SARS_CoV_2 <- as.integer(blanco_data.df$`padj_SARS-CoV-2(A549)` < 0.05 & # low p-value
abs(blanco_data.df$`SARS-CoV-2(A549)_L2FC`) > 1.5) # absolute L2FC > 1.5
# We'll name each element in our vector by the gene names
names(...) <- blanco_data.df$GeneName
# What is our object?
str(genes_SARS_CoV_2)
# Summarize our vector
table(genes_SARS_CoV_2)
So a quick bit of filtering and we can see from our table summary,
that we have close to 500 genes that are significantly differentially
expressed above or below 1.5 L2FC. Now we have to figure out how to map
these to GO terms!
4.1.1 Choose the correct database to map your gene symbols to GO
terms
The goseq package is compatible with a number of
organisms and gene names for mapping your data to the GO database. To
view which organisms are supported, we can use
supportedOrganisms() to view. Since the raw RNA-Seq reads
were aligned to the human genome using the hg19 assembly (see
Blanco-Melo et al., 2020), we’ll be working with hg19 as
well. We’ll use the annotation information provided for the next part of
our analysis.
First, however, let’s check that hg19 is indeed a supported genome
annotation.
# Pull down the supported organisms and look for the human "hg19" genome
supportedOrganisms() %>%
filter(...)
4.1.2 Fitting a probability weighting function (PWF) to our
dataset
Looking at our table above we can immediately see that there are 3
potential annotations sets to draw from. Each uses a different kind of
ID description: Entrez Gene ID, Ensemble gene ID, and Gene Symbol. Which
one do we use? This depends on how our data is formatted but let’s take
a quick look at this screenshot of CCL8 entry from NCBI:

It’s important to know the difference between your gene symbols and
various identifiers!
Knowing what we’ve seen of the data already, the information we’re
looking for is in the third row. Using the gene symbols in our data, we
can
- Map our gene symbols to GO annotations, and
- Gather the gene lengths for our data.
The second portion of information is required in the generation of a
probability weighting function (PWF). This PWF will determine a
weighting for each gene which is essentially the probability of a gene
being differentially expressed based on its length alone. It’s important
to generate with information as it can influence our enrichment
analysis. Ultimately we use the PWF to help inform the creation of a
null distribution in our analysis.
We’ll use the nullp() function to generate the PWF for
our dataset.
# Generate the PWF for our vector of genes
pwf_SARS_CoV_2 <- nullp(DEgenes = ..., # Our list of DE genes
genome = ..., # The GO annotation we want to use
id = ...) # The id we'll use to generate the data using gene symbols
head(pwf_SARS_CoV_2)
Looking at a plot of our PWF: here we see a subset
of genes and their PWF values to see how they fit according to our line
of best fit. The goal is for the points to be relatively close to our
line of fit. If the values are far off, there may be an issue with the
annotation data or how you calculated your gene lengths for the PWF.
4.1.3 Generate enriched GO terms from our DE data
We’re now ready to query the GO database for up- and down-regulated
terms in our data. We’ll use the goseq() command to do
this, providing our PWF object as well as the correct database
information. The default method used in this process to generate our
enrichment analysis is the Wallenius
approximation. You can choose to use random sampling but
this method can take much longer with little difference in results.
For more information on this process see the goseq
package vignette here
# Generate our go enrichment analysis
GO.wall_SARS_CoV_2 <- goseq(pwf = ..., # Provide our pwf object
genome = "hg19", # Set the annotation set to use
id = "geneSymbol", # Which variable will we use to compare with
method = ... # How will you decide on the null distribution
)
# What does the final result look like?
head(GO.wall_SARS_CoV_2)
# What is the structure of our resulting data?
str(GO.wall_SARS_CoV_2)
4.2.0 Interpreting our goseq results
Looking at the results of our analysis, we are returned a listing of
22,500 unique GO terms across the 3 domains (CC, BP, and MF). We are
given two sets of p-values, one each for over-represented and
under-represented terms. With each term we also see how many times that
term is represented in our DE data vs the total number of times that
term may appear in the GO database we queried. As you can see from the
first few rows of data, some GO term categories can encompass a large
number of genes.
Remember how we created this data! Let’s also remind
ourselves that we set up our DE gene list using the data from A459 cells
infected with SARS-CoV-2. Looking at the manuscript itself, while having
a distinct immune response, the replication of the virus was very low in
these cell types which lack the expression of the ACE2 receptor. We’re
really just using this data to make some visualizations!
To begin our analysis we can filter the enriched GO terms sets by
applying a multiple hypothesis test correction with
p.adjust() using the Benjamini-Hochberg
method to minimize the false discovery rate.
# Generate an enriched Go term dataset
enriched.GO_SARS_CoV_2 <-
# Adjust the p-values of the over-represented column
# GO.wall_SARS_CoV_2[p.adjust(GO.wall_SARS_CoV_2$over_represented_pvalue, method="BH") < 0.05, ]
GO.wall_SARS_CoV_2 %>%
filter(...)
# How many terms come back from our filtering?
str(enriched.GO_SARS_CoV_2)
# Load our goseq data to look at and visualize
# This will also contain some additional datasets we'll want to look at.
load("./data/Lecture04.RData")
# What kind of terms do we see in our enrichment?
enriched.GO_SARS_CoV_2$...
4.2.1 Plot our enriched GO term categories as a dot plot
Looking at our enriched set of GO terms, we see terms like
inflammatory response and response to stress.
Now that we have a set of enriched GO terms, we can filter and plot this
information as a dotplot with the following characteristics:
- GO terms listed along the y-axis
- size of dots representing category size
- use colour to represent the % of DE genes present from that that
category
Let’s look at terms that include the word “response”.
enriched.GO_SARS_CoV_2 %>%
# Generate a percentage value
mutate(percentOfCat = numDEInCat/numInCat,
# Create a dummy category
# pathogen = "SARS-CoV-2",
# Make a set of adjusted p-values
FDR = p.adjust(over_represented_pvalue, method = "BH")
) %>%
# Filter for only terms that include the word "response"
filter(str_detect(term, "response")) %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
### 4.2.1 set aesthetics by numDEInCat and FDR
aes(x="SARS-CoV-2", y=..., size=..., colour = ...) +
# Theme
theme_bw() +
theme(text = element_text(size=20)) +
# 3. Scaling
scale_size(range=c(3,10)) +
scale_colour_viridis_c(direction = -1) +
# 4. Geoms
geom_point()
4.2.2 Plot multiple conditions to the same dot plot
With a little bit of elbow grease, we can generate a data frame with
multiple GO enrichment analyses for multiple experiments and
now we can compare enrichment across data sets! Our
Lecture04.RData contained the combined experimental GO
results in the object enriched.GO_combined.df.
# Take a look at our combined data frame
enriched.GO_combined.df
# Load up a combined enrichment analysis
# load("./data/Lecture04.RData")
enriched.GO_combined.df %>%
# 1. Data
ggplot(.) +
# 2. Aesthetics
### 4.2.4 Set the new x-axis value and colour aesthetic
aes(x=..., y=term, size=numDEInCat, colour = ...) +
# Theme
theme_bw() +
theme(text = element_text(size=20)) +
# 3. Scaling
scale_size(range=c(3,10)) +
scale_colour_viridis_c(direction = -1) +
# 4. Geoms
geom_point()
4.3.0 Upset plots
4.3.1 Can Upset plots help us to make sense of overlapping
relationships?
Upset plots are an alternative to Venn diagrams that show the
intersection of sets, as well as the size of the sets. Additionally,
Venn diagrams can be difficult to interpret for greater than 2 or 3
sets. This is a real life figure from BMC Bioinformatics. Sure it looks
pretty, but what does the number 24 represent in this picture in terms
of A, B, C, D, and E?

What is the meaning of the value 24 in this diagram? Stare at it long
enough and you’ll see which group it’s in but imagine this was 10
groups?
The UpSet plot was first published in 2014 and has
become a helpful tool for visualizing the intersection between
components with datasets. Along with the publication came a package for
working with these plots. Known as the UpSetR
package, there have since been more projects implementing this kind
of visualization. While UpSetR has not been updated in
nearly 3 years, a more active package known as ComplexUpset
is available.
To some extent, the ComplexUpset has extensibility with
ggplot, allowing you to use somewhat familiar syntax to
modify these plots. Add to this the ability to stack or include
additional visualizations of your datasets distributions or other
characteristics, and this is a pretty attractive package to work
with.
4.3.2 Working with the ComplexUpset package to
visualize overlapping datasets
Let’s see how UpSet plots work practically. We can compare our data
from blanco_data_long.df and compare the overlap of DE
genes (after filtering or not). To do this in a practical sense, we
again need to convert our values to a boolean representation after some
more data wrangling. Our data wrangling steps will include:
- Generating a short-list of genes with a high log2
fold-change and low p-value.
- Filtering our data to include only those genes.
- Converting our data to a wide format where each column represents
the inclusion status of a gene (observation) for a specific
experiment.
Let’s look at our data structure again.
head(blanco_data_long.df)
Begin by making our short-list of genes to filter by.
# filter for upregulated genes in our SARS-CoV-2/A549 data set and make a list of gene names
SARS_CoV_2.list <-
blanco_data_long.df %>%
# Filter our data
filter(pathogen == "SARS-CoV-2",
host == "A549",
...) %>%
# Select just our gene names using a shorthand conversion
pull(GeneName)
# Less code than the following steps:
# dplyr::select(GeneName) %>% unlist() %>% as.character()
SARS_CoV_2.list
Filter our blanco_data_long.df and pivot to a
wide-format containing our logical values for inclusion.
# Generate our upset data
blanco_upset.df <-
blanco_data_long.df %>%
# Filter our data list using our genes of interest
filter(GeneName %in% ...) %>%
# convert our L2FC data to a logical based on a value of >2
mutate(upregulated = ...) %>%
# Select just the gene names, experiments, and the logical variable
dplyr::select(GeneName, experiment, ...) %>%
# Pivot our data wide so that each GeneName is now a row, each experiment is a column
pivot_wider(names_from = ..., values_from = ...) %>%
# Convert to a dataframe for the ComplexUpset package (or it will throw an error)
as.data.frame()
str(blanco_upset.df)
4.3.3 Generating our upset data
As you can see from our data transformations, we now have all of our
experiments listed in columns, with each gene represented in each row.
As we look down each column we see a value of TRUE or
FALSE used to differentiate if there was overexpression of
that gene in that specific experimental context.
If we had not filtered based on some set of genes, we would have a
data frame with more than 23K columns! Now that we have our genes
presented in this way we can proceed to generate an upset plot.
Working with the upset() function to build our plot, we
will concern ourselves with the following parameters:
data: a dataframe including binary columns that
represent membership in each class.
intersect: a list of column names which will be used to
generate the intersecting data.
name: the label shown below the intersection
matrix.
height_ratio: ratio of the intersection matrix to the
intersection height (default = 0.5).
width_ratio: ratio of the overall set size width to
intersection matrix width (default = 0.3).
min_size and max_size: minimal and maximal
number of observations for an intersection to be included.
min_degree and max_degree: minimal and
maximal number of degrees for an intersection to be included.
n_intersections: the maximum number of intersections to
display based on our min_* or max_*
criteria.
themes: a parameter used to pass theme changes through
the upset_default_themes() or
upset_modify_themes() functions.
# Generate our upset plot
upset(data = ..., # Provide our dataset
intersect = ..., # Name the columns we want to analyse
name='Experimental condition', # Set the label below the intersection matrix
width_ratio=0.1, # Make the Set size width a little smaller
min_size = ..., # Require there to be a minimum of 2 members to show an intersection
n_intersections = ..., # Set the max number of intersections we want to plot
themes = upset_default_themes(text = element_text(size = 20)) # Set the plot text size to 20
)
4.3.4 Interpreting our Upset plot
From our upset plot, we can see there are 3 regions.
The left-hand barplot denotes the total number of observations in
each set/category. In this case we’re talking about experimental
conditions.
The bottom plot graphically represents the different combinations
of each category.
The upper barplot displays the number of occurences for the
combination displayed in the bottom plot.
From our set sizes, it looks like a L2FC value of > 2 was perhaps
too stringent for the IAV and MERS-CoV DE data. Since we did filter our
genes initially by the best hits in our SARS-COV-2(A549) dataset, we can
see that it has the highest frequency group at 34 genes with over
expression in just this set. The next largest overlapping set is of 14
genes between the SARS-CoV-2(A549) set and the RSV(A549) DE data.
You can also see from the plot that there are only 9 combinations of
datasets with any real overlap. Had we set min_size = 1
instead, the remainder of the grouping combinations would be seen to
contain just a single gene in each.
Overall this can make for a simplified interpretation of overlapping
genes versus more complicated Venn diagrams!
5.0.0 Class summary
Today we took a long look at some popular visualizations for
expression data sets generated by an experiment like RNAseq. Our
analyses focused more on highlighting aspects of the differential
expression information itself from a big-picture low-resolution view
like volcano plots to zooming down to the individual gene level with dot
plots.
Next week we’ll look at examples of “big” data from an even broader
sense by using dimensional reduction techniques like classifying our
datasets into groups with principle component analysis.
5.1.0 Weekly assignment
This week’s assignment will be found under the current lecture folder
under the “assignment” subfolder. It will include an R markdown notebook
that you will use to produce the code and answers for this week’s
assignment. Please provide answers in markdown or code cells that
immediately follow each question section.
| Code |
50% |
- Does it follow best practices? |
|
|
- Does it make good use of available packages? |
|
|
- Was data prepared properly |
| Answers and Output |
50% |
- Is output based on the correct dataset? |
|
|
- Are groupings appropriate |
|
|
- Are correct titles/axes/legends correct? |
|
|
- Is interpretation of the graphs correct? |
Since coding styles and solutions can differ, students are encouraged
to use best practices. Assignments may be rewarded for
well-coded or elegant solutions.
You can save and download the markdown notebook in its native format.
Submit this file to the the appropriate assignment section by 12:59 pm
on the date of our next class: April 11th, 2024.
5.2.0 Acknowledgements
Revision 1.0.0: created and prepared for
CSB1021H S LEC0141, 03-2021 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.1: edited and prepared for
CSB1020H S LEC0141, 03-2022 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 1.0.2: edited and prepared for
CSB1020H S LEC0141, 03-2023 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
Revision 2.0.0: Revised and prepared for
CSB1020H S LEC0141, 03-2024 by Calvin Mok,
Ph.D. Bioinformatician, Education and Outreach, CAGEF.
The Center for the Analysis of Genome Evolution and Function
(CAGEF)
The Centre for the Analysis of Genome Evolution and Function (CAGEF)
at the University of Toronto offers comprehensive experimental design,
research, and analysis services in microbiome and metagenomic studies,
genomics, proteomics, and bioinformatics.
From targeted DNA amplicon sequencing to transcriptomes, whole
genomes, and metagenomes, from protein identification to
post-translational modification, CAGEF has the tools and knowledge to
support your research. Our state-of-the-art facility and experienced
research staff provide a broad range of services, including both
standard analyses and techniques developed by our team. In particular,
we have special expertise in microbial, plant, and environmental
systems.
For more information about us and the services we offer, please visit
https://www.cagef.utoronto.ca/.
LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMgTGVjdHVyZSAwNDogRXhwcmVzc2luZyBleHByZXNzaW9uIGRhdGENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMS4wIEFuIG92ZXJ2aWV3IG9mIEFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUg0KDQoqKiJBZHZhbmNlZCBHcmFwaGljcyBhbmQgRGF0YSBWaXN1YWxpemF0aW9uIGluIFIiKiogaXMgYnJvdWdodCB0byB5b3UgYnkgdGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gJiBGdW5jdGlvbidzIChDQUdFRikgYmlvaW5mb3JtYXRpY3MgdHJhaW5pbmcgaW5pdGlhdGl2ZS4gVGhpcyBDU0IxMDIxIHdhcyBkZXZlbG9wZWQgdG8gZW5oYW5jZSB0aGUgc2tpbGxzIG9mIHN0dWRlbnRzIHdpdGggYmFzaWMgYmFja2dyb3VuZHMgaW4gUiBieSBmb2N1c2luZyBvbiBhdmFpbGFibGUgcGhpbG9zb3BoaWVzLCBtZXRob2RzLCBhbmQgcGFja2FnZXMgZm9yIHBsb3R0aW5nIHNjaWVudGlmaWMgZGF0YS4gV2hpbGUgdGhlIGRhdGFzZXRzIGFuZCBleGFtcGxlcyB1c2VkIGluIHRoaXMgY291cnNlIHdpbGwgYmUgY2VudHJlZCBvbiBTQVJTLUNvVi0yIGVwaWRlbWlvbG9naWNhbCBhbmQgZ2Vub21pYyBkYXRhLCB0aGUgbGVzc29ucyBsZWFybmVkIGhlcmVpbiB3aWxsIGJlIGJyb2FkbHkgYXBwbGljYWJsZS4NCg0KVGhpcyBsZXNzb24gaXMgdGhlIGZvdXJ0aCBpbiBhIDYtcGFydCBzZXJpZXMuIFRoZSBhaW0gZm9yIHRoZSBlbmQgb2YgdGhpcyBzZXJpZXMgaXMgZm9yIHN0dWRlbnRzIHRvIHJlY29nbml6ZSBob3cgdG8gaW1wb3J0LCBmb3JtYXQsIGFuZCBkaXNwbGF5IGRhdGEgYmFzZWQgb24gdGhlaXIgaW50ZW5kZWQgbWVzc2FnZSBhbmQgYXVkaWVuY2UuIFRoZSBmb3JtYXQgYW5kIHN0eWxlIG9mIHRoZXNlIHZpc3VhbGl6YXRpb25zIHdpbGwgaGVscCB0byBpZGVudGlmeSBhbmQgY29udmV5IHRoZSBrZXkgbWVzc2FnZShzKSBmcm9tIHRoZWlyIGV4cGVyaW1lbnRhbCBkYXRhLg0KDQpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhICoqY29kZS1hbG9uZyBzdHlsZSoqIGluIFIgbWFya2Rvd24gbm90ZWJvb2tzLiBBdCB0aGUgc3RhcnQgb2YgZWFjaCBsZWN0dXJlLCBza2VsZXRvbiB2ZXJzaW9ucyBvZiB0aGUgbGVjdHVyZSB3aWxsIGJlIHByb3ZpZGVkIGZvciB1c2Ugb24gdGhlIFtVbml2ZXJzaXR5IG9mIFRvcm9udG8gZGF0YXRvb2xzIEh1Yl0oaHR0cHM6Ly9kYXRhdG9vbHMudXRvcm9udG8uY2EpIHNvIHN0dWRlbnRzIGNhbiBwcm9ncmFtIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjIuMCBMZWN0dXJlIG9iamVjdGl2ZXMNCg0KVGhpcyB3ZWVrIHdpbGwgZm9jdXMgb24gc3RhbmRhcmQgdmlzdWFsaXphdGlvbnMgb2YgZXhwcmVzc2lvbiBkYXRhLiBXaGlsZSBvdXIgcHJpb3IgdG9waWNzIGhhdmUgZm9jdXNlZCBvbiBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdGhhdCBhcmUgYXBwbGljYWJsZSBicm9hZGx5IHRvIG1hbnkgdHlwZXMgb2YgZGF0YSwgYSBudW1iZXIgb2YgdGhpcyB3ZWVrJ3MgdmlzdWFsaXphdGlvbnMgYXJlIHVzZWQgbWFpbmx5IG9uIG11bHRpLWRpbWVuc2lvbmFsIGRhdGEgZnJvbSBleHBlcmltZW50cyBsaWtlIFJOQXNlcS4NCg0KQXQgdGhlIGVuZCBvZiB0aGlzIGxlY3R1cmUgeW91IHdpbGwgaGF2ZSBjb3ZlcmVkIHRoZSBmb2xsb3dpbmcgdG9waWNzDQoNCjEuICBWaXN1YWxpemluZyBmbG93Y2hhcnRzIGFuZCB3b3JrZmxvd3MNCjIuICBTY2F0dGVycGxvdCB2YXJpYW50cyBvZiBSTkFzZXEgZGF0YQ0KMy4gIENvbG91ciBncmFkaWVudCB2aXN1YWxpemF0aW9ucyBha2EgaGVhdG1hcHMNCjQuICBSTkFzZXEgZGF0YSBhbmFseXNpcyB3aXRoIGBnb3NlcWANCjUuICBWaXN1YWxpemluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gc2FtcGxlcw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4zLjAgQSBsZWdlbmQgZm9yIHRleHQgZm9ybWF0IGluIFIgbWFya2Rvd24NCg0KYGdyZXkgYmFja2dyb3VuZGAgLSBhIHBhY2thZ2UsIGZ1bmN0aW9uLCBjb2RlLCBjb21tYW5kIG9yIGRpcmVjdG9yeS4gQmFja3RpY2tzIGFyZSBhbHNvIHVzZSBmb3IgaW4tbGluZSBjb2RlLlwNCippdGFsaWNzKiAtIGFuIGltcG9ydGFudCB0ZXJtIG9yIGNvbmNlcHQgb3IgYW4gaW5kaXZpZHVhbCBmaWxlIG9yIGZvbGRlclwNCioqYm9sZCoqIC0gaGVhZGluZyBvciBhIHRlcm0gdGhhdCBpcyBiZWluZyBkZWZpbmVkXA0KW2JsdWUgdGV4dF17c3R5bGU9ImNvbG9yOmJsdWUifSAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rDQoNCmAuLi5gIC0gV2l0aGluIGVhY2ggY29kaW5nIGNlbGwgdGhpcyB3aWxsIGluZGljYXRlIGFuIGFyZWEgb2YgY29kZSB0aGF0IHN0dWRlbnRzIHdpbGwgbmVlZCB0byBjb21wbGV0ZSBmb3IgdGhlIGNvZGUgY2VsbCB0byBydW4gY29ycmVjdGx5Lg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKkJsdWUgYm94OioqIEEga2V5IGNvbmNlcHQgdGhhdCBpcyBiZWluZyBpbnRyb2R1Y2VkDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipZZWxsb3cgYm94OioqIFJpc2sgb3IgY2F1dGlvbg0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqR3JlZW4gYm94ZXM6KiogUmVjb21tZW5kZWQgcmVhZHMgYW5kIHJlc291cmNlcyB0byBsZWFybiBQeXRob24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqUmVkIGJveGVzOioqIEEgY29tcHJlaGVuc2lvbiBxdWVzdGlvbiB3aGljaCBtYXkgb3IgbWF5IG5vdCBpbnZvbHZlIGEgY29kaW5nIGNlbGwuIFlvdSB1c3VhbGx5IGZpbmQgdGhlc2UgYXQgdGhlIGVuZCBvZiBhIHNlY3Rpb24uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNC4wIExlY3R1cmUgYW5kIGRhdGEgZmlsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZQ0KDQojIyMgMC40LjEgV2Vla2x5IExlY3R1cmUgYW5kIHNrZWxldG9uIGZpbGVzDQoNCkVhY2ggd2VlaywgbmV3IGxlc3NvbiBmaWxlcyB3aWxsIGFwcGVhciB3aXRoaW4geW91ciBSU3R1ZGlvIGZvbGRlcnMuIFdlIGFyZSBwdWxsaW5nIGZyb20gYSBHaXRIdWIgcmVwb3NpdG9yeSB1c2luZyB0aGlzIFtSZXBvc2l0b3J5IGdpdC1wdWxsIGxpbmtdKGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvaHViL3VzZXItcmVkaXJlY3QvZ2l0LXB1bGw/cmVwbz1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZjYW1vayUyRjIwMjQtMDMtQWR2X0dyYXBoaWNzX1ImdXJscGF0aD1yc3R1ZGlvJTJGJmJyYW5jaD1tYWluKS4gU2ltcGx5IGNsaWNrIG9uIHRoZSBsaW5rIGFuZCBpdCB3aWxsIHRha2UgeW91IHRvIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKS4gWW91IHdpbGwgbmVlZCB0byB1c2UgeW91ciBVVE9SaWQgY3JlZGVudGlhbHMgdG8gY29tcGxldGUgdGhlIGxvZ2luIHByb2Nlc3MuIEZyb20gdGhlcmUgeW91IHdpbGwgZmluZCBlYWNoIHdlZWsncyBsZWN0dXJlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYC8yMDI0LTAzLUFkdl9HcmFwaGljc19SL0xlY3R1cmVfWFhgLiBZb3Ugd2lsbCBmaW5kIGEgcGFydGlhbGx5IGNvZGVkIGBza2VsZXRvbi5SbWRgIGZpbGUgYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRhdGEgZmlsZXMgbmVjZXNzYXJ5IHRvIHJ1biB0aGUgd2VlaydzIGxlY3R1cmUuDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIFItTWFya2Rvd24gTm90ZWJvb2sgKGAuUm1kYCkgYW5kIGRhdGEgZmlsZXMgZnJvbSB0aGUgUlN0dWRpbyBzZXJ2ZXIgdG8geW91ciBwZXJzb25hbCBjb21wdXRlciBpZiB5b3Ugd291bGQgbGlrZSB0byBydW4gaW5kZXBlbmRlbnRseSBvZiB0aGUgVG9yb250byB0b29scy4NCg0KIyMjIDAuNC4yIExpdmUtY29kaW5nIEhUTUwgcGFnZQ0KDQpBIGxpdmUgbGVjdHVyZSB2ZXJzaW9uIHdpbGwgYmUgYXZhaWxhYmxlIGF0IFtjYW1vay5naXRodWIuaW9dKGh0dHBzOi8vY2Ftb2suZ2l0aHViLmlvLzIwMjQtMDMuQWR2X0dyYXBoaWNzX1IvaW5kZXguaHRtbCkgdGhhdCB3aWxsIHVwZGF0ZSBhcyB0aGUgbGVjdHVyZSBwcm9ncmVzc2VzLiBCZSBzdXJlIHRvIHJlZnJlc2ggdG8gdGFrZSBhIGxvb2sgaWYgeW91IGdldCBsb3N0IQ0KDQojIyMgMC40LjMgUG9zdC1sZWN0dXJlIFBERnMNCg0KQXMgbWVudGlvbmVkIGFib3ZlLCBhdCB0aGUgZW5kIG9mIGVhY2ggbGVjdHVyZSB0aGVyZSB3aWxsIGJlIGEgY29tcGxldGVkIHZlcnNpb24gb2YgdGhlIGxlY3R1cmUgY29kZSByZWxlYXNlZCBhcyBhIFBERiBmaWxlIHVuZGVyIHRoZSBNb2R1bGVzIHNlY3Rpb24gb2YgUXVlcmN1cy4NCg0KIyMjIDAuNC40IERhdGEgdXNlZCBpbiB0aGlzIGxlc3Nvbg0KDQpUb2RheSdzIGRhdGFzZXRzIHdpbGwgZm9jdXMgb24gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuIFRoZSBiYXNpYyB2aXN1YWxpemF0aW9ucyBvZiB0aGlzIGRhdGEgYWZ0ZXIgaXQgaGFzIGFscmVhZHkgYmVlbiBwcm9jZXNzZWQgYnkgcGFja2FnZXMgbGlrZSBgREVTZXEyYC4NCg0KIyMjIDAuNC40LjEgRGF0YXNldCAxOiBMZWN0dXJlMDRfc2Fua2V5X2RhdGEuY3N2DQoNClRoaXMgaXMgYW4gZXhhbXBsZSBkYXRhc2V0IHVzZWQgZm9yIGJ1aWxkaW5nIGEgU2Fua2V5IGRpYWdyYW0uIEl0IGJ1aWxkcyBhIHRoZW9yZXRpY2FsIHdvcmtmbG93IGZvciBSTkFzZXEgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gd2l0aGluIGEgbWFudXNjcmlwdC4gV2hhdCBpcyBhIFNhbmtleSBkaWFncmFtPyBXZSdsbCBmaW5kIG91dCENCg0KIyMjIDAuNC40LjIgRGF0YXNldCAyOiBXeWxlcjIwMjBfQUVDX1NBUlNDb1YyXzE3QUFHX3JlYWRjb3VudHMudHN2DQoNClJOQS1TZXEgcmVhZCBjb3VudCBkYXRhIGdlbmVyYXRlZCBmcm9tIFNBUlMtQ29WMiBpbmZlY3Rpb25zIG9mIEFFQyBjZWxscy4gVXNlZCB0byBjb21wYXJlIHRoZSB0aW1lY291cnNlIG9mIGV4cHJlc3Npb24gKHByby1pbmZsYW1tYXRvcnkpIGNoYW5nZXMgaW4gc2FtcGxlcyB0cmVhdGVkIHdpdGggYW5kIHdpdGhvdXQgSFNQOTAgaW5oaWJpdG9ycy4gUHVibGlzaGVkIGluICppU2NpZW5jZSogZG9pOiA8aHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5pc2NpLjIwMjEuMTAyMTUxPg0KDQojIyMgMC40LjQuMyBEYXRhc2V0IDM6IEJsYW5jby1NZWxvMjAyMENlbGwuU3VwcDEueGxzeA0KDQpUaGlzIGlzIGFuIFJOQXNlcSBkYXRhc2V0IGNvbXBhcmlzb24gZm9yIGluZmVjdGlvbiBvZiBtdWx0aXBsZSBjZWxsIHR5cGVzIHdpdGggcGF0aG9nZW5zIGxpa2UgU0FSUy1Db1YtMiwgU0FSUy1Db1YtMSwgTUVSUy1Db1YsIFJTViAocmVzcGlyYXRvcnkgc3luY3l0aWFsIHZpcnVzKSwgSUFWIChpbmZsdWVuemEgQSB2aXJ1cykgYW5kIEhQSVYzIChodW1hbiBwYXJhaW5mbHVlbnplIHZpcnVzIHR5cGUgMykgcHVibGlzaGVkIGluICpDZWxsKiBkb2k6IDEwLjEwMTYvai5jZWxsLjIwMjAuMDQuMDI2DQoNCiMjIyAwLjQuNC40IERhdGFzZXQgNDogQmxhbmNvLU1lbG8yMDIwX1N1cHBfRGF0YV80Lnhsc3gNCg0KVGhpcyBpcyBhbiBSTkFzZXEgZGF0YXNldCBjb21wYXJpc29uIGZvciBpbmZlY3Rpb24gb2YgaHVtYW4gYWx2ZW9sYXIgYWRlbm9jYXJjaW5vbWEgKEE1NDkpIGNlbGxzIHdpdGggYW5kIHdpdGhvdXQgaW5mbHVlbnphIEEgdmlydXMgKElBVikgcHVibGlzaGVkIG9uICpiaW9SeGl2KiBkb2k6IDxodHRwczovL2RvaS5vcmcvMTAuMTEwMS8yMDIwLjAzLjI0LjAwNDY1NT4NCg0KIyMjIDAuNC40LjUgRGF0YXNldCA1OiBMZWN0dXJlMDQuUkRhdGENCg0KQSBzYXZlZCBmaWxlIHdpdGggYSBmaW5hbCBHTyBhbm5vdGF0aW9uIGRhdGFzZXQgd2UgbG9vayBhdCB3aXRoIG91ciB2aXN1YWxpemF0aW9ucyBvZiBkb3QgcGxvdHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjUuMCBQYWNrYWdlcyB1c2VkIGluIHRoaXMgbGVzc29uDQoNCmB0aWR5dmVyc2VgIHdoaWNoIGhhcyBhIG51bWJlciBvZiBwYWNrYWdlcyBpbmNsdWRpbmcgYGRwbHlyYCwgYHRpZHlyYCwgYHN0cmluZ3JgLCBgZm9yY2F0c2AgYW5kIGBnZ3Bsb3QyYA0KDQpgdmlyaWRpc2AgaGVscHMgdG8gY3JlYXRlIGNvbG9yLWJsaW5kIHBhbGV0dGVzIGZvciBvdXIgZGF0YSB2aXN1YWxpemF0aW9ucw0KDQpgbmV0d29ya0QzYCwgYGdnaGlnaGxpZ2h0YCwgYGdncmVwZWxgLCBhbmQgYFVwU2V0UmAgYXJlIHVzZWQgaW4gYnVpbGRpbmcgc29tZSBvZiBvdXIgdmlzdWFsaXphdGlvbnMgaW5jbHVkaW5nIGZsb3djaGFydHMsIGFuZCB1cHNldCBwbG90cy4NCg0KYGdvc2VxYCwgYW5kIGBvcmcuSHMuZWcuZGJgIGFyZSBwYWNrYWdlcyB0aGF0IHdpbGwgaGVscCB1cyBwZXJmb3JtIHNvbWUgYmFzaWMgR2VuZSBPbnRvbG9neSAoR08pIGFubm90YXRpb25zIHdpdGggb3VyIGRhdGEuDQoNCmBgYHtyfQ0KIyAjIEluc3RhbGwgdGhlc2UgcGFja2FnZXMgb250byByLmRhdGF0b29scw0KIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQojIGluc3RhbGwucGFja2FnZXMoIm5ldHdvcmtEMyIpDQojIGluc3RhbGwucGFja2FnZXMoImdnaGlnaGxpZ2h0IikNCiMgaW5zdGFsbC5wYWNrYWdlcygiQ29tcGxleFVwc2V0IiwgdHlwZT0ic291cmNlIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dyZXBlbCIpDQojIA0KIyANCiMgIyAyMjAzMzE6IEN1cnJlbnQgdmVyc2lvbiBvZiBKdXB5dGVySHViIGlzIG5vdCBhbGxvd2luZyBnb3NlcSB0byBiZSBpbnN0YWxsZWQgY29ycmVjdGx5DQojIA0KIyAjICMgU29tZSBwYWNrYWdlcyBjYW4gYmUgaW5zdGFsbGVkIHZpYSBCaW9jb25kdWN0b3INCiMgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiMgICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwodmVyc2lvbiA9ICIzLjE4IikNCiMgDQojICMgVGhpcyBzaG91bGQgaW5zdGFsbCBnb3NlcSAxLjU0LjAuIENoZWNrIHRoaXMgdW5kZXIgdGhlIHBhY2thZ2VzIHRhYiENCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoImdvc2VxIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoIm9yZy5Icy5lZy5kYiIpDQojIA0KIyAjIFRoaXMgbGFzdCBwYXJ0IHdpbGwgaW5zdGFsbCBzb21lIG9sZGVyIHZlcnNpb24gcGFja2FnZXMgc28gdGhhdCBDb21wbGV4VXBzZXQgd2lsbCBydW4gcHJvcGVybHkNCiMgZGV2dG9vbHM6Omluc3RhbGxfdmVyc2lvbigiZHBseXIiLCB2ZXJzaW9uID0gIjEuMS4zIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQojIGRldnRvb2xzOjppbnN0YWxsX3ZlcnNpb24oImdncGxvdDIiLCB2ZXJzaW9uID0gIjMuNC40IiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KKioqDQpXZSdyZSBwbGF5aW5nIGZhc3QgYW5kIGxvb3NlIHdpdGggc29tZSBpbnN0YWxsYXRpb25zIGhlcmUgdG8gZ2V0IGl0IHRvIHdvcmsgb24gci5kYXRhdG9vbHMudXRvcm9udG8uY2Egc28gYWZ0ZXIgdGhlIGluc3RhbGxhdGlvbiBpcyBjb21wbGV0ZSwgcmVzdGFydCB5b3VyIHNlc3Npb24gd2l0aCBgU2Vzc2lvbiA+IFJlc3RhcnQgUmAuIERvbid0IHJlZnJlc2ggeW91ciBicm93c2VyIG9yIHlvdSdsbCBsb3NlIHRoZSBpbnN0YWxsYXRpb24uDQoNCmBgYHtyfQ0KIyBQYWNrYWdlcyB0byBoZWxwIHRpZHkgb3VyIGRhdGENCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQoNCiMgUGFja2FnZXMgZm9yIHRoZSBncmFwaGljYWwgYW5hbHlzaXMgc2VjdGlvbg0KbGlicmFyeSh2aXJpZGlzKQ0KDQojIExlY3R1cmUgMDQgdmlzdWFsaXphdGlvbiBwYWNrYWdlcw0KbGlicmFyeShuZXR3b3JrRDMpDQpsaWJyYXJ5KEdHYWxseSkNCmxpYnJhcnkoZ2doaWdobGlnaHQpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KENvbXBsZXhVcHNldCkNCmBgYA0KDQoqKioNCkxvYWQgdGhlc2UgbGlicmFyaWVzIHNlcGFyYXRlbHkgdG8gc2F2ZSBvbiBhIGxpdHRsZSBiaXQgb2YgbWVtb3J5IHVzYWdlLiBXZSdsbCBuZWVkIGV2ZXJ5IGxhc3QgZHJvcCB3ZSBjYW4gZ2V0Lg0KDQpgYGB7cn0NCiMgR08gdGVybSBhbmFseXNpcyBwYWNrYWdlcyBpZiB5b3UgbWFuYWdlZCB0byBpbnN0YWxsIGdvc2VxDQpsaWJyYXJ5KGdvc2VxKQ0KbGlicmFyeShvcmcuSHMuZWcuZGIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMS4wLjAgV29ya2luZyB3aXRoIGxhcmdlIGFtb3VudHMgb2YgZGF0YQ0KDQpVbnRpbCB0aGlzIHBvaW50LCB0aGUgZGF0YSBzZXRzIHdlIGhhdmUgdXNlZCBpbiBsZWN0dXJlIGhhdmUgYmVlbiByZWxhdGl2ZWx5IHNpbXBsZSBlcGlkZW1pb2xvZ2ljYWwgaW5mb3JtYXRpb24uIEFsbCBvZiBvdXIgb2JzZXJ2YXRpb25zIGFyZSB2YWx1ZXMgbGlrZSBuZXcgY2FzZXMgb3IgdmFjY2luYXRlZCBpbmRpdmlkdWFscyB0aWVkIHRvIGRhdGVzIG9yIHRpbWUgcGVyaW9kcy4gV2hlcmVhcyBtb3N0IG9mIG91ciBvYnNlcnZhdGlvbnMgd2VyZSBjb25uZWN0ZWQgaW4gc29tZSBsaW5lYXIgZmFzaGlvbiwgdGhlIGRhdGEgd2UgZXhhbWluZSB0b2RheSB3aWxsIGhhdmUgdGVucyBvZiB0aG91c2FuZHMgb2Ygb2JzZXJ2YXRpb25zLCBlYWNoIHJlcHJlc2VudGluZyBhIGRpZmZlcmVudCBnZW5lIQ0KDQpHaXZlbiB0aGUgY29tcGxleGl0eSBvZiBvdXIgZGF0YSwgd2Ugd2lsbCBkaXNjdXNzIHdheXMgdG8gc29ydCwgcGFydGl0aW9uLCB2aXN1YWxpemUgYW5kIGludGVycHJldCBpdC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMS4wIFdoZXJlIGRvZXMgUk5Bc2VxIGRhdGEgY29tZSBmcm9tPw0KDQpNYW55IG9mIHlvdSBtYXkgYmUgZmFtaWxpYXIgd2l0aCB0aGUgaWRlYSBvZiBSTkFzZXEgZGF0YS4gSXQgYmVnaW5zIGluIHRoZSByZWFsIHdvcmxkIChvciBhdCB0aGUgYmVuY2gpIHdpdGggaW5kaXZpZHVhbHMvZ3JvdXBzL2NvbmRpdGlvbnMgY29tcGFyZWQgYWdhaW5zdCBhIGNvbnRyb2wgc3RhdGUuIEJpb2xvZ2ljYWwgc2FtcGxlcyBjYW4gYmUgY29sbGVjdGVkIGluIHRpbWUgc2VyaWVzIG9yIGF0IGEgc2luZ2xlIGVuZHBvaW50LiBFYWNoIGV4cGVyaW1lbnRhbCBjb25kaXRpb24gc2hvdWxkIGhhdmUgaXQncyBtYXRjaGluZyBjb250cm9sIG9yIGJhc2VsaW5lIHByb2ZpbGUgZm9yIGNvbXBhcmlzb24sIG5vdCB0byBtZW50aW9uIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyENCg0KQWZ0ZXIgY29sbGVjdGluZyB5b3VyIHNhbXBsZXMgeW91IHN0aWxsIG5lZWQgdG8gcHJlcGFyZSBhbmQgc2VxdWVuY2UgeW91ciBsaWJyYXJpZXMsIGNoZWNrIHlvdXIgZGF0YSBxdWFsaXR5LCB0cmltIHJlYWRzLCBtYXAgdGhlbSB0byBhIHRyYW5zY3JpcHRvbWUsIGVzdGltYXRlL25vcm1hbGl6ZSB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lcyB3aXRoaW4gZWFjaCBsaWJyYXJ5IGFuZCAqdGhlbiogY29tcGFyZSB0aGUgZXhwcmVzc2lvbiBiZXR3ZWVuIGxpYnJhcmllcy9zYW1wbGVzIHRvIGRldGVybWluZSBpZiB0aGVyZSBhcmUgdHJhbnNjcmlwdGlvbmFsIGRpZmZlcmVuY2VzISBBbGwgb2YgdGhlc2Ugc3RlcHMgYXJlIGJleW9uZCB0aGlzIGNsYXNzIEJVVCBpZiB5b3UgcGxhbiBvbiB3b3JraW5nIHdpdGggdHJhbnNjcmlwdG9tZXMsIHlvdSdsbCBldmVudHVhbGx5IGVuY291bnRlciB0aGlzIHByb2Nlc3MuIFRvZGF5IHdlJ2xsIGp1bXAgdGhlIGxpbmUgYW5kIHdvcmsgZGlyZWN0bHkgd2l0aCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGRhdGEuIFRoYXQgbWVhbnMgd2UnbGwgYmUgYXQgdGhlIGJvdHRvbSByaWdodCBjb3JuZXIgb2YgdGhlIGZvbGxvd2luZyBkaWFncmFtIEFLQSwgdGhlIGVuZCBvZiB0aGUgcGlwZWxpbmUuDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovUk5Bc2VxXzEucG5nP3Jhdz10cnVlIiB3aWR0aD0iOTAwIi8+DQoNCkltYWdlIGZyb20gQmlvaW5mb3JtYXRpY3MgV29ya2Jvb2s6IFtSTkEgU2VxdWVuY2luZyBBbmFseXNpc10oaHR0cHM6Ly9iaW9pbmZvcm1hdGljc3dvcmtib29rLm9yZy9kYXRhQW5hbHlzaXMvUk5BLVNlcS9STkEtU2VxSW50cm8vUk5Bc2VxLXVzaW5nLWEtZ2Vub21lLmh0bWwjZ3NjLnRhYj0wKQ0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjIuMCBVc2UgYSBTYW5rZXkgZGlhZ3JhbSB0byBpbGx1c3RyYXRlIHlvdXIgd29ya2Zsb3cNCg0KU2Fua2V5IGRpYWdyYW1zIGFyZSBuYW1lZCBhZnRlciBJcmlzaCBDYXB0YWluIE1hdHRoZXcgSGVucnkgUGhpbmVhcyBSaWFsbCBTYW5rZXksIHdobyBmaXJzdCBwcmVzZW50ZWQgdGhpcyB0eXBlIG9mIHZpc3VhbGl6YXRpb24gaW4gMTg5OCB0byBjb252ZXkgdGhlIGVuZXJneSBlZmZpY2llbmN5IG9mIGEgc3RlYW0gZW5naW5lIQ0KDQpXaXRoIGxhcmdlciBzZXRzIG9mIGRhdGEgYWNyb3NzIG11bHRpcGxlIGV4cGVyaW1lbnRzLCBpdCBjYW4gc29tZXRpbWVzIGJlIGhlbHBmdWwgdG8gdXNlIGEgU2Fua2V5IGRpYWdyYW0gdG8gZXhwbGFpbiB0aGUgY29ubmVjdGlvbiBiZXR3ZWVuIGRpZmZlcmVudCBhc3BlY3RzIG9mIHlvdXIgc2FtcGxlcy4gVGhlIGtleSBhdHRyaWJ1dGUgb2YgdGhlIFNhbmtleSBkaWFncmFtIGlzIHRoYXQgdGhlIHdpZHRoIG9mIGEgY29ubmVjdGlvbiAoZmxvdykgaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSBxdWFudGl0eSByZXByZXNlbnRlZC4gU28gdGhlIFNhbmtleSBkaWFncmFtIGlzIGEgZmxvdyBkaWFncmFtIHRoYXQgYWxzbyB2aXN1YWxseSBxdWFudGlmaWVzIHRoZSBkeW5hbWljcyBvZiB0aGUgcmVsYXRpb25zaGlwcyBpbiB5b3VyIGRpYWdyYW0uIFRoaW5rIG9mIGl0IG11Y2ggbGlrZSBhIHN0YWNrZWQgYmFyIGNoYXJ0IHRoYXQgY2FuIHNwbGl0IG9yIGpvaW4gd2l0aCBvdGhlciBiYXJzIGF0IGVhY2ggY29ubmVjdGlvbi4NCg0KQSB1c2VmdWwgcGFja2FnZSB0byBnZW5lcmF0ZSBTYW5rZXkgZGlhZ3JhbXMgaXMgYG5ldHdvcmtEM2AgdXNpbmcgdGhlIGBzYW5rZXlOZXR3b3JrKClgIGZ1bmN0aW9uLiBUbyBnZW5lcmF0ZSBhIFNhbmtleSBkaWFncmFtLCB5b3UgbmVlZCBpbmZvcm1hdGlvbiBvbiB0d28gdmFyaWFibGVzIHdpdGggYSB0aGlyZCBvcHRpb25hbCBvbmU6DQoNCi0gICBgc291cmNlYDogYSBzdGFydGluZyBwb2ludCBvciBmb3IgeW91ciBmbG93DQotICAgYHRhcmdldGA6IHRoZSBlbmQgcG9pbnQgb2YgeW91ciBmbG93DQotICAgYHZhbHVlYDogdGhlIG51bWJlciB0aGF0IHdpbGwgcmVwcmVzZW50IHRoZSB3aWR0aCBvZiB5b3VyIGZsb3cuIFVzdWFsbHkgc29tZSBxdWFudGl0eSBvZiBjb250cmlidXRpb24gYnV0IHRoaXMgY2FuIGFsc28gYmUgYW4gb3B0aW9uYWwgdmFyaWFibGUuDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovc2Fua2V5cGxvdC5wbmc/cmF3PXRydWUiIHdpZHRoPSI5MDAiLz4NCg0KV2UnbGwgdGFrZSB0aGUgdGltZSB0byByZWNvbnN0cnVjdCB0aGlzIHNhbmtleSBwbG90IGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMuDQo6OjoNCg0KV2UnbGwgZmluZCBhIHByZS1tYWRlIHRhYmxlIHRoYXQgZGVzY3JpYmVzIGEgdGhlb3JldGljYWwgUk5Bc2VxIHdvcmtmbG93IGJhc2VkIGluIHRoZSBkYXRhc2V0cyB3ZSdsbCBiZSB3b3JraW5nIHdpdGggdG9kYXkuIFNvbWUgc2VjdGlvbnMgaGF2ZSBiZWVuIGNvbXByZXNzZWQgZm9yIGJyZXZpdHkgYnV0IHdlJ2xsIGZpbmQgYWxsIG9mIHRoYXQgaW5mb3JtYXRpb24gaW4gYC4vZGF0YS9MZWN0dXJlMDRfc2Fua2V5X2RhdGEuY3N2YC4gTGV0J3MgYmVnaW4gYnkgcmVhZGluZyBpdCBpbi4NCg0KYGBge3J9DQojIFJlYWQgaW4gb3VyIFNhbmtleSBkaWFncmFtIGluIC4vZGF0YS9MZWN0dXJlMDRfc2Fua2V5X2RhdGEuY3N2IA0KDQpzYW5rZXlfaW5mby5kZiA8LSByZWFkX2NzdiguLi4pDQoNCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZQ0Kc3RyKHNhbmtleV9pbmZvLmRmKQ0KDQojIExvb2sgYSBmZXcgcm93cw0KaGVhZChzYW5rZXlfaW5mby5kZikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KRnJvbSB0aGUgYWJvdmUgb3V0cHV0IHdlIGNhbiBzZWUgdGhhdCBvbmUgYHNvdXJjZWAgbWF5IGhhdmUgbXVsdGlwbGUgYHRhcmdldGAgdmFsdWVzIGFuZCB2aWNlIHZlcnNhLg0KDQojIyMgMS4yLjEgQ29udmVydCB5b3VyIGZsb3cgZGF0YSBmcmFtZSBpbnRvIGEgbm9kZSBkYXRhIGZyYW1lDQoNCkFsdGhvdWdoIG91ciBkYXRhIGZyYW1lIGBzYW5rZXlfaW5mby5kZmAgY29udGFpbnMgdGhlIGluZm9ybWF0aW9uIGFib3V0IG91ciBjb25uZWN0aW9ucywgdGhlIGBuZXR3b3JrRDNgIHBhY2thZ2UgcmVxdWlyZXMgc29tZSBtYXBwaW5nIG9mIHRoZSBub2RlcyB3aXRoaW4gb3VyIGRhdGEuIEVhY2ggdW5pcXVlIHNvdXJjZSBhbmQgdGFyZ2V0IHdpbGwgYmUgY291bnRlZCBhcyBhIG5vZGUgYW5kIHdlJ2xsIHNhdmUgdGhhdCBpbmZvcm1hdGlvbiBpbnRvIGBzYW5rZXlfbm9kZXMuZGZgLg0KDQpgYGB7cn0NCiMgQ29tYmluZSBvdXIgc291cmNlIGFuZCB0YXJnZXQgbGlzdHMsIHRoZW4gdGFrZSB0aGUgdW5pcXVlIHNldCBhbmQgcHV0IHRoYXQgaW50byB0aGUgIm5hbWUiIHZlY3Rvci4NCg0KIyBzYW5rZXlfbm9kZXMuZGYgPC0gZGF0YS5mcmFtZShuYW1lPWMoYXMuY2hhcmFjdGVyKHNhbmtleV9pbmZvLmRmJHNvdXJjZSksICAgICAgIyBMb2NhdGlvbiBvZiBvdXIgc291cmNlIG5vZGVzDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoc2Fua2V5X2luZm8uZGYkdGFyZ2V0KSkgJT4lICAjIExvY2F0aW9uIG9mIG91ciB0YXJnZXQgbm9kZXMNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKCkgIyBHZW5lcmF0ZSB0aGUgdW5pcXVlIGNvbWJpbmF0aW9uIG9mIHRoZSB0d28gc2V0cw0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KIyBDb21iaW5lIG91ciB0YXJnZXQgYW5kIHNvdXJjZSBub2RlcyBpbnRvIGEgc2luZ2xlIHZlY3Rvcg0Kc2Fua2V5X25vZGVzLmRmIDwtIGMoYXMuY2hhcmFjdGVyKC4uLiksDQogICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoLi4uKSkgJT4lDQogIA0KICAjIE9ubHkga2VlcCB0aGUgdW5pcXVlIHZhbHVlcw0KICB1bmlxdWUoKSAlPiUNCiAgDQogICMgUHV0IHRoaXMgaW50byBhIGRhdGEgZnJhbWUgdW5kZXIgYSBjb2x1bW4gY2FsbGVkICJuYW1lIg0KICBkYXRhLmZyYW1lKG5hbWUgPSAuKQ0KDQoNCiMgTG9vayBhdCB0aGUgc3RydWN0dXJlLCBpdCdzIGp1c3QgYSBsaXN0IG9mIHRoZSBub2Rlcw0Kc3RyKHNhbmtleV9ub2Rlcy5kZikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMi4yIE1hcCB5b3VyIG5vZGUgbmFtZXMgYmFjayB0byB5b3VyIHNvdXJjZSBhbmQgdGFyZ2V0IGRhdGEgd2l0aCBgbWF0Y2goKWANCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgbm9kZSBpbmZvcm1hdGlvbiAoMTggdG90YWwgdW5pcXVlIG5vZGVzKSBzdG9yZWQgYmFzaWNhbGx5IGFzIGFuIGlkZW50aWZpY2F0aW9uIG51bWJlciwgd2UgaGF2ZSB0byBtYXAgaXQgYmFjayB0byBgc2Fua2V5X2luZm8uZGZgLiBUaGlzIGlzIGVzc2VudGlhbGx5IGxpa2Ugd29ya2luZyB3aXRoIGEgZmFjdG9yIHRoYXQgc3BhbnMgdHdvIGNvbHVtbnMgaW5zdGVhZCBvZiBqdXN0IG9uZS4gVG8gYWNjb21wbGlzaCB0aGUgbWFwcGluZywgd2UnbGwgdXNlIHRoZSBgbWF0Y2goKWAgbWV0aG9kLiBUaGUgYG1hdGNoKClgIGZ1bmN0aW9uIHRha2VzIHR3byB2YWx1ZXM6DQoNCi0gICBgeGA6IHRoZSB2YWx1ZXMgdG8gYmUgbWF0Y2hlZA0KDQpgdGFibGVgOiB0aGUgdmFsdWVzIHRvIGJlIG1hdGNoZWQgYWdhaW5zdA0KDQphbmQgcmV0dXJucyBhIHZlY3RvciBvZiBsZW5ndGggYHhgIHdoZXJlIGVhY2ggaW50ZWdlciB2YWx1ZSByZXByZXNlbnRzIHRoZSBwb3NpdGlvbiBvZiBpdHMgbWF0Y2hlZCB2YWx1ZSBmcm9tIGB0YWJsZWAuIFVubWF0Y2hlZCB2YWx1ZXMgZnJvbSBgeGAgKGllIG5vdCBmb3VuZCBpbiBgdGFibGVgKSBhcmUgc2V0IHRvIGBub21hdGNoYC4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KKiptYXRjaCgpIHZzICIlaW4lIioqOiBUaGUgKiptYXRjaCgpKiogZnVuY3Rpb24gYW5kIHRoZSBtYXRjaGluZyBvcGVyYXRvciAqKiVpbiUqKiBhcmUgdmVyeSBzaW1pbGFyIGluIGlkZWEuIFdoZXJlYXMgdGhlIGZvcm1lciBwcm9kdWNlZCBhIHNldCBvZiBtYXRjaGVkIHBvc2l0aW9ucyBiZXR3ZWVuIHNldHMsIHRoZSBsYXR0ZXIgcHJvZHVjZXMgYSBsb2dpY2FsIHZlY3RvciBpbmRpY2F0aW5nIGluY2x1c2lvbiAoVFJVRSkgb3IgZXhjbHVzaW9uIChGQUxTRSkgZnJvbSB0aGUgaW50ZXJzZWN0IG9mIHRoZSBzZXRzLg0KOjo6DQoNCkxldCdzIHRyeSB3aXRoIGEgcXVpY2sgZXhhbXBsZSB3aXRoIG91ciBkYXRhDQoNCmBgYHtyfQ0KIyBXaGljaCBub2RlcyBtYXRjaCBmcm9tIG91ciBzb3VyY2Ugbm9kZXMgdG8gb3VyIGNvbGxlY3RlZCBub2RlcyAoMTggdG90YWwgbm9kZXMpPw0KbWF0Y2goc2Fua2V5X2luZm8uZGYkLi4uLCBzYW5rZXlfbm9kZXMuZGYkLi4uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpOb3RlIHRoYXQgb3VyIG5vZGUgaW5mb3JtYXRpb24gbXVzdCBiZSAwLWluZGV4ZWQhIFIgdXNlcyBhbiBpbmRleGluZyBzY2hlbWUgdGhhdCBiZWdpbnMgYXQgMSBzbyB3ZSdsbCBuZWVkIHRvIG9mZnNldCB0aGF0IGluZm9ybWF0aW9uIHRoYXQgd2UgZ2VuZXJhdGUgYXMgd2VsbC4NCg0KYGBge3J9DQojIEFkZCBvdXIgc2Fua2V5X25vZGVzLmRmIGFzIGluZGV4IGluZm9ybWF0aW9uIHRoYXQgbWFwcyBiZXR3ZWVuIGl0IGFuZCBgc2Fua2V5X2luZm8uZGZgDQpzYW5rZXlfaW5mby5kZiA8LQ0Kc2Fua2V5X2luZm8uZGYgICU+JSBtdXRhdGUoSURzb3VyY2UgPSAuLi4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBJRHRhcmdldCA9IC4uLikgIyBub2RlcyBtdXN0IGJlIDAtaW5kZXhlZA0KDQojIHRha2UgYSBwZWVrIGF0IHdoYXQgd2UndmUgd3JvdWdodA0KaGVhZChzYW5rZXlfaW5mby5kZikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMi4zIEJ1aWxkIHlvdXIgU2Fua2V5IGRpYWdyYW0gd2l0aCBgc2Fua2V5TmV0d29yaygpYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIG91ciB0d28gbmVjZXNzYXJ5IGRhdGEgZnJhbWVzLCB3ZSBjYW4gYnVpbGQgb3VyIFNhbmtleSBkaWFncmFtIHdpdGggdGhlIGBzYW5rZXlOZXR3b3JrKClgIGZ1bmN0aW9uLiBUaGlzIGZ1bmN0aW9uIGhhcyBhIGZldyBwYXJhbWV0ZXJzIHRoYXQgd2UnbGwgd2FudCB0byBzZXQ6DQoNCi0gICBgTGlua3NgOiB0aGUgbG9jYXRpb24gb2Ygb3VyIHNvdXJjZS90YXJnZXQgcmVsYXRpb25zaGlwIGluZm9ybWF0aW9uIChgc2Fua2V5X2luZm8uZGZgKS4NCg0KLSAgIGBOb2Rlc2A6IHRoZSBsaXN0aW5nIG9mIG5vZGVzIGluIG91ciBkaWFncmFtIChgc2Fua2V5X25vZGVzLmRmYCkuDQoNCi0gICBgU291cmNlYDogdGhlIG5hbWUgb2YgdGhlIHNvdXJjZSBjb2x1bW4gKGBJRHNvdXJjZWApLg0KDQotICAgYFRhcmdldGA6IHRoZSBuYW1lIG9mIG91ciB0YXJnZXQgY29sdW1uIChgSUR0YXJnZXRgKS4NCg0KLSAgIGBWYWx1ZWA6IHRoZSBuYW1lIG9mIG91ciAib3B0aW9uYWwiIHZhbHVlIHRvIGJ1aWxkIHRoZSB3aWR0aCBvZiBmbG93cyAoYHZhbHVlYCkuDQoNCi0gICBgTm9kZUlEYDogdGhlIG5hbWVzIG9mIG91ciBub2RlcyAoYHNhbmtleV9ub2Rlcy5kZiRuYW1lYCkuDQoNClRoZXJlIGFyZSBhbiBhZGRpdGlvbmFsIG51bWJlciBvZiBwYXJhbWV0ZXJzIHRoYXQgYWxsb3cgeW91IHRvIHBsYXkgd2l0aCB0aGUgdmlzdWFsaXphdGlvbiBvZiB0aGlzIGRpYWdyYW0gaW5jbHVkaW5nOg0KDQotICAgYGNvbG91clNjYWxlYDogY2F0ZWdvcmljYWwgY29sb3VyIG9mIHlvdXIgbm9kZS4gV29ya3MgdG9nZXRoZXIgd2l0aC4uLg0KDQotICAgYE5vZGVHcm91cGA6IGZvciBjb2xvdXJpbmcgeW91ciBub2RlcyBiYXNlZCBvbiBhbiBleHRyYSBjb2x1bW4gaW4gYE5vZGVzYC4NCg0KLSAgIGBMaW5rR3JvdXBgOiBmb3IgY29sb3VyaW5nIHlvdXIgbGlua3MgYmFzZWQgb24gYW4gZXh0cmEgY29sdW1uIGluIGBMaW5rc2AuDQoNCi0gICBgbm9kZVdpZHRoYDogdGhlIG51bWVyaWMgd2lkdGggb2YgZWFjaCBub2RlLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGVzZSBwYXJhbWV0ZXJzIGFuZCBvdGhlcnMsIHlvdSBjYW4gZ28gW2hlcmVdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9uZXR3b3JrRDMvdmVyc2lvbnMvMC40L3RvcGljcy9zYW5rZXlOZXR3b3JrKQ0KDQpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQ9Nn0NCiMgQnVpbGQgdGhlIFNhbmtleSBkaWFncmFtIHdpdGggYWxsIG9mIG91ciBpbmZvcm1hdGlvbg0KDQpzYW5rZXlOZXR3b3JrKExpbmtzID0gLi4uLCAjIFdoZXJlIGlzIHRoZSAiZWRnZSIgaW5mb3JtYXRpb24NCiAgICAgICAgICAgICAgTm9kZXMgPSAuLi4sICMgV2hlcmUgaXMgdGhlICJub2RlIiBpbmZvcm1hdGlvbg0KICAgICAgICAgICAgICBTb3VyY2UgPSAuLi4sICMgV2hlcmUgYXJlIHRoZSBzb3VyY2Ugbm9kZSBsYWJlbHMNCiAgICAgICAgICAgICAgVGFyZ2V0ID0gLi4uLCAjIFdoZXJlIGFyZSB0aGUgdGFyZ2V0IG5vZGUgbGFiZWxzDQogICAgICAgICAgICAgIFZhbHVlID0gLi4uLCAjIFdoYXQgdmFsdWUgZG8gd2UgYXNzaWduIHRvIHRoZSByZWxhdGlvbnNoaXBzL2VkZ2VzDQogICAgICAgICAgICAgIE5vZGVJRCA9IC4uLiwgIyBXaGVyZSBhcmUgdGhlIGxhYmVscyBmb3IgdGhlIG5vZGVzIHN0b3JlZD8NCiAgICAgICAgICAgICAgc2lua3NSaWdodCA9IFRSVUUsICMgUHVzaCB0aGUgbm9kZXMgdG8gdGhlIHJpZ2h0DQogICAgICAgICAgICAgIGZvbnRTaXplID0gMTApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMy4wIFNjYXR0ZXJwbG90IG1hdHJpY2VzIGZvciBzb21lIHF1aWNrIFFBIG9mIHlvdXIgcmVhZCBjb3VudHMNCg0KU3RlcHBpbmcgYmFjayBvbmUgc3RlcCBpbiBvdXIgZmxvd2NoYXJ0LCBqdXN0IHByaW9yIHRvIHlvdXIgREUgYW5hbHlzaXMsIHlvdSdsbCBoYXZlIGEgc2V0IG9mIHJlYWQgY291bnRzIGdlbmVyYXRlZCBmcm9tIFJOQS1TZXEgZGF0YS4gVGhlIGNvdW50cyBhcmUgYW4gZXN0aW1hdGlvbiBvZiBlYWNoIHRyYW5zY3JpcHQgd2l0aGluIHlvdXIgZGF0YS4gU29tZSBwcm9ncmFtcyBtYXkgaW5jbHVkZSB0aGUgYW5hbHlzaXMgb2Ygc3BsaWNlZCBpc29mb3JtcyBhbmQgb3RoZXJzIG1heSBub3QuDQoNCk9mdGVuIGluIHlvdXIgUk5BLVNlcSBkYXRhc2V0cywgeW91IHNob3VsZCBoYXZlIG11bHRpcGxlIHJlcGxpY2F0ZXMgb2YgeW91ciBkYXRhLiBBIHF1aWNrIHdheSB0byBhc3Nlc3MgdGhlIHF1YWxpdHkgb2YgeW91ciBkYXRhc2V0cyBpcyB0byBnZW5lcmF0ZSBhIHNjYXR0ZXJwbG90IG1hdHJpeC4gVGhlIHNjYXR0ZXJwbG90IG1hdHJpeCBpcyBhbiBhbGwtYnktYWxsIGFzc2Vzc21lbnQgb2YgeW91ciBleHBlcmltZW50cyB0aGF0IGdlbmVyYXRlcyBhIHNjYXR0ZXJwbG90IG9mIHJlYWQgY291bnRzIGZvciBlYWNoIHBhaXItd2lzZSBkYXRhc2V0Lg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6LzEyODU5XzIwMTlfMjk2OF9GaWczX0hUTUwud2VicD9yYXc9dHJ1ZSIgd2lkdGg9IjgwMCIvPg0KDQpBIHNjYXR0ZXJwbG90IG1hdHJpeCBjYW4gaGVscCB0byBxdWlja2x5IGFzc2VzcyB0aGUgcXVhbGl0eSBhbmQgY29uc2lzdGVuY3kgb2YgeW91ciBkYXRhLg0KDQpGaWd1cmUgdGFrZW4gZnJvbTogVmlzdWFsaXphdGlvbiBtZXRob2RzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gKlJ1dHRlciBldCBhbC4sIDIwMTkuIEJNQyBCaW9pbmZvcm1hdGljcyAyMDogNDU4Kg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4zLjEgSW1wb3J0IHlvdXIgd2lkZS1mb3JtYXQgcmVhZCBjb3VudCBkYXRhLg0KDQpXZSdsbCBiZWdpbiBieSBpbXBvcnRpbmcgb3VyIGRhdGEgZnJvbSBgV3lsZXIyMDIwX0FFQ19TQVJTQ29WMl8xN0FBR19yZWFkY291bnRzLnRzdmAuIFRoaXMgdGFiLXNlcGFyYXRlZCBkYXRhIGZpbGUgY29udGFpbnMgdG90YWwgUk5BIGV4cGVyaW1lbnRzIGZyb20gdGhlIGluZmVjdGlvbiBvZiBhaXJ3YXkgZXBpdGhlbGlhbCBjZWxscyAoQUVDcykgdW5kZXIgZGlmZmVyZW50IGNvbmRpdGlvbnMsIHRyZWF0bWVudHMgd2l0aCBpbmhpYml0b3JzLCBhbmQgdGltZXBvaW50cy4NCg0KYGBge3J9DQojIFJlYWQgaW4geW91ciByZWFkX2NvdW50IGRhdGENCnd5bGVyX3JlYWRjb3VudHMuZGYgPC0gcmVhZF90c3YoLi4uKQ0KDQpzdHIod3lsZXJfcmVhZGNvdW50cy5kZiwgZ2l2ZS5hdHRyID0gRkFMU0UpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMiBQcmVwYXJlIHlvdXIgZGF0YSBhcyBhIGNsZWFuIHJlYWQgY291bnQgbWF0cml4DQoNCkJlZm9yZSB3ZSBwcm9jZWVkIHRvIGdlbmVyYXRpbmcgb3VyIHNjYXR0ZXJwbG90IG1hdHJpeCwgd2UgbmVlZCB0byBzZWxlY3Qgb3VyIGRhdGEgZnJvbSBvdXIgbWFpbiBkYXRhc2V0LiBJbiBwYXJ0aWN1bGFyLCB0aGUgZGF0YSBmcm9tICpXeWxlciBldCBhbC4qLCB0aGF0IHdlIGltcG9ydGVkIGFzIGB3eWxlcl9yZWFkY291bnRzLmRmYCBjb250YWlucyBkYXRhIGZyb20gaW5mZWN0aW5nIGh1bWFuIGFpcndheSBlcGl0aGVsaWFsIGNlbGxzIChBRUNzKSBhbmQgdHJlYXRpbmcgd2l0aCBlaXRoZXIgKipETVNPKiogb3IgMjAwbk0gb2YgdGhlIEhTUDkwIGluaGliaXRvciwgKioxNy1BQUcqKi4gV2l0aCBlYWNoIGNvbmRpdGlvbiB0aGVyZSBpcyBhIHNldCBvZiBpbmZlY3RlZCAoKipTMioqKSBvciB1bmluZmVjdGVkIEFFQ3MuIEZvciBlYWNoIG9mIHRoZXNlIDQgY29uZGl0aW9ucywgdGhlcmUgYXJlIDMgcmVwbGljYXRlcyBhbmQgMyB0aW1lcG9pbnRzICgqKjI0LSoqLCAqKjQ4LSoqLCBhbmQgKio3Mi0qKmhvdXJzKS4gSW4gdG90YWwgdGhhdCBhbW91bnRzIHRvIDM2IHNldHMgb2YgZGF0YS4NCg0KRm9yIG91ciBwdXJwb3Nlcywgd2UnbGw6DQoNCjEuICBDb21wYXJlIDI0aCB2cyA3MmggaW5mZWN0ZWQgKGllIFMyID0gU0FSUy1Db1YyKSBETVNPLXRyZWF0ZWQgZm91bmQgaW4gY29sdW1ucyA5LTExIGFuZCAzMy0zNS4gV2UgY2FuIGVpdGhlciBzZWxlY3QgYnkgaW5kZXggb3IgdXNlIGEgc2VsZWN0aW9uIGhlbHBlciBsaWtlIGBtYXRjaGVzKClgIHRvIGluY29ycG9yYXRlIGEgcmVndWxhciBleHByZXNzaW9uLg0KDQoyLiAgRmlsdGVyIG91ciByZWFkcyB0byB0aGUgcmFuZ2Ugb2YgMTAtNTAwMCB1c2luZyB0aGUgYGlmX2FsbCgpYCBmdW5jdGlvbi4NCg0KMy4gIDNcLiBSZW5hbWUgb3VyIGNvbHVtbnMgdG8gcmVtb3ZlIHNvbWUgcmVkdW5hbnQgZGF0YSAoYEFFQ0lJX3h4X2ApDQoNCk5vdGUgdGhhdCB0aGUgYGlmX2FsbCgpYCBmdW5jdGlvbiBpcyBhIHJhdGhlciBuZXcgYWRkaXRpb24gdG8gdGhlIHRpZHl2ZXJzZSBhbmQgaXMgdXNlZCB0byBoZWxwIGZpbHRlciBvbiBhIHByZWRpY2F0ZSBhY3Jvc3MgKm11bHRpcGxlKiBjb2x1bW5zLiBUaGlzIGlzIHZlcnkgbXVjaCBsaWtlIHRoZSBgYWNyb3NzKClgIGhlbHBlciBmdW5jdGlvbiB5b3UgbWF5IGhhdmUgdXNlZCBwcmV2aW91c2x5IGZvciBzdW1tYXJpemluZyBkYXRhLiBMaWtlIHRoZSBgYWNyb3NzKClgIGZ1bmN0aW9uLCB0aGUgYGlmX2FsbCgpYCBoZWxwZXIgZnVuY3Rpb24gdXNlcyB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgLmNvbHNgOiB0aGUgY29sdW1ucyB5b3Ugd2lzaCB0byBhcHBseSB5b3VyIGZpbHRlcmluZyBwcmVkaWNhdGUgdXBvbi4NCg0KLSAgIGAuZm5zYDogdGhlIGZ1bmN0aW9uIG9yIHByZWRpY2F0ZSB5b3UgYXJlIGZpbHRlcmluZyB3aXRoLg0KDQpZb3UnbGwgbm90aWNlIG91ciB1c2Ugb2YgYH5gIGFnYWluIHRvIGFub255bWl6ZSB0aGUgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KcmVhZGNvdW50c18yNGh2NzJoIDwtDQogICAgd3lsZXJfcmVhZGNvdW50cy5kZiAlPiUgDQoNCiAgICAjIFNlbGVjdCBmb3IganVzdCB0aGUgY29sdW1ucyB3aXRocyBTQVJTLUNvVi0yIGluZmVjdGlvbiBhbmQgRE1TTyBhdCAyNCBhbmQgNzJoDQogICAgZHBseXI6OnNlbGVjdCguLi4pICU+JSANCg0KICAgICMgUmVzdHJpY3Qgb3VyIGRhdGEgYW5hbHlzaXMgdG8ganVzdCByZWFkY291bnRzIGFib3ZlIDEwIGFuZCBiZWxvdyA1MDAwDQogICAgZmlsdGVyKGlmX2FsbCguY29scyA9IC4uLiwgLmZucyA9IH4gLnggPjEwICYgLnggPCAuLi4pKSAlPiUgDQoNCiAgICAjIFJlbmFtZSB0aGUgY29sdW1ucyBieSByZW1vdmluZyB0aGUgZmlyc3QgcG9ydGlvbjogQUVDSUlfeHgNCiAgICByZW5hbWVfd2l0aCguLCB+IHN0cl9yZXBsYWNlKHN0cmluZyA9IC54LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSByIihcdypfXGQqXykiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSAiIikpDQoNCiMgVGFrZSBhIHBlZWsgYXQgb3VyIHJlc3VsdGluZyB0aWJibGUNCnN0cihyZWFkY291bnRzXzI0aHY3MmgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjMuMyBVc2UgYGdncGFpcnMoKWAgdG8gZ2VuZXJhdGUgYSBzY2F0dGVycGxvdCBtYXRyaXgNCg0KWW91IG1heSByZWNhbGwgb3VyIHdvcmtpbmcgd2l0aCB0aGUgcGFyYWxsZWwgY29vcmRpbmF0ZSBwbG90IGZyb20gKipsZWN0dXJlIDIqKi4gUmF0aGVyIHRoYW4gdHJhbnNmb3JtIHRoZSBkYXRhIG91cnNlbHZlcywgd2UgcGFzc2VkIG91ciBkYXRhIGFsb25nIHRvIHRoZSBgZ2dwYXJjb29yZCgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgR0dhbGx5YCBwYWNrYWdlLiBXZWxsIHRoYXQgcGFja2FnZSBpcyBoZXJlIHRvIHNpbXBsaWZ5IG91ciBsaXZlcyBhZ2FpbiEgUmF0aGVyIHRoYW4gZ2VuZXJhdGluZyB0aGUgcGFpcmVkIGRhdGEgYmV0d2VlbiBlYWNoIGV4cGVyaW1lbnQgb3Vyc2VsdmVzLCB3ZSBjYW4gcGFzcyBhbG9uZyB0aGUgcmVhZCBjb3VudCBtYXRyaXggd2UganVzdCBnZW5lcmF0ZWQgdG8gdGhlIGBnZ3BhaXJzKClgIGZ1bmN0aW9uLiBQYXJhbWV0ZXJzIG9mIHRoZSBgZ2dwYWlycygpYCBmdW5jdGlvbiB0aGF0IHdlJ2xsIHVzZSBpbmNsdWRlOg0KDQotICAgYGRhdGFgOiB0aGUgZGF0YXNldCBjb250YWluaW5nIG91ciBkYXRhLiBJdCBjYW4gaW5jbHVkZSBib3RoIG51bWVyaWNhbCBhbmQgY2F0ZWdvcmljYWwgZGF0YS4NCi0gICBgbWFwcGluZ2A6IHRoZSBhZXN0aGV0aWMgbWFwcGluZyAoYXNpZGUgZnJvbSBgeGAgYW5kIGB5YCkgdXNlZCBmb3IgYWx0ZXJpbmcgeW91ciBmb3JtYXQuDQotICAgYGNvbHVtbnNgOiB3aGljaCBjb2x1bW5zIGZyb20gYGRhdGFgIGFyZSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBwbG90LiBEZWZhdWx0cyB0byBhbGwgY29sdW1ucy4NCi0gICBgdGl0bGVgLCBgeGxhYmAsIGB5bGFiYDogdGhlIHRpdGxlcyBvZiB5b3VyIHZhcmlvdXMgZ3JhcGggY29tcG9uZW50cy4NCi0gICBgdXBwZXJgIGFuZCBgbG93ZXJgOiBuYW1lZCBsaXN0cyB0aGF0IG1heSBjb250YWluIHRoZSB2YXJpYWJsZXMgImNvbnRpbnVvdXMiLCAiY29tYm8iLCAiZGlzY3JldGUiLCBhbmQgIm5hIiB1c2VkIHRvIGRldGVybWluZSBob3cgcGFpcndpc2UgY29tYmluYXRpb25zIG9mIGNvbnRpbnVvdXMgYW5kL29yIGNhdGVnb3JpY2FsIGRhdGEgYXJlIHBsb3R0ZWQgYWNyb3NzIHRoZSBncmlkLiBZb3UgYmFzaWNhbGx5IG5lZWQgdG8gY2hvb3NlIGhvdyB0aGVzZSBkYXRhIGNvbWJpbmF0aW9ucyB3aWxsIGJlIHBsb3R0ZWQuDQotICAgYGRpYWdgOiBhIG5hbWVkIGxpc3QgdGhhdCBtYXkgb25seSBjb250YWluIHRoZSB2YXJpYWJsZXMgImNvbnRpbnVvdXMiLCAiZGlzY3JldGUiLCBhbmQgIm5hIi4gRWFjaCBvZiB3aGljaCBpcyBzZXQgdG8gYSBzcGVjaWZpYyBraW5kIG9mIHBsb3QgdHlwZSAoaWUgImRlbnNpdHlEaWFnIiwgImJhckRpYWciLCBvciAiYmxhbmtEaWFnIikuDQoNCk1vcmVvdmVyLCB5b3UgbWF5IHJlY2FsbCB0aGF0IGBHR2FsbHlgIHdvcmtzIHdpdGggdGhlIGBnZ3Bsb3RgIG9yIGlzIGBnZ3Bsb3RgLSpleHRlbnNpYmxlKiBzbyB3ZSBjYW4gdXNlIG1hbnkgb2YgdGhlIHNhbWUgZmVhdHVyZXMgZnJvbSBnZ3Bsb3QgdG8gZml4IHNvbWUgb2YgdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIHJlc3VsdCBvYmplY3QuIEluIHRoZSBlbmQsIHlvdSdsbCBub3RpY2UgdGhhdCB0aGlzIGlzIGJhc2ljYWxseSBhIGZhY2V0ZWQgc2NhdHRlcnBsb3QgYnV0IHRoZSBkYXRhZnJhbWUgcmVxdWlyZWQgdG8gZ2VuZXJhdGUgaXQgaXMgbW9yZSBjb21wbGV4IHRoYW4gd2hhdCB3ZSBjdXJyZW50bHkgaGF2ZS4gRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhpcyBmdW5jdGlvbiwgeW91IGNhbiBjaGVjayBvdXQgdGhlIFtgR0dhbGx5IG1hbnVhbGBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9HR2FsbHkvR0dhbGx5LnBkZikgb3IgY2hlY2sgb3V0IHRoZSBbbGlzdCBvZiBncmVhdCBleGFtcGxlcyBwcm92aWRlZCBvbiB0aGUgYXV0aG9ycycgZ2l0aHViXShodHRwczovL2dnb2JpLmdpdGh1Yi5pby9nZ2FsbHkvcmVmZXJlbmNlL2dncGFpcnMuaHRtbCkuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTR9DQoNCiMgQ3JlYXRlIHlvdXIgbWF0cml4IHNjYXR0ZXJwbG90IHdpdGggZ2dwYWlycw0KZ2dwYWlycyhyZWFkY291bnRzXzI0aHY3MmgsIA0KICAgICAgICANCiAgICAgICAgIyBTZXQgdGhlIGFscGhhIG9mIG91ciBwb2ludHMgc28gd2UgY2FuIHNlZSB0aGVtIGJldHRlcg0KICAgICAgICBtYXBwaW5nID0gLi4uLCANCiAgICAgICAgDQogICAgICAgICMgV2UnbGwgcGxheSB3aXRoIHRoZSBjb3JyZWxhdGlvbiB0ZXh0IHNvIHRoZSB2YWx1ZXMgYXJlIGxpdHRsZSBsYXJnZXIgdGhhbiBkZWZhdWx0IGZvciB1cw0KICAgICAgICB1cHBlciA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImNvciIsIHNpemUgPSA5KSkNCiAgICAgICApICsgIyBTZXQgdGhlIGFscGhhIG9mIG91ciBwb2ludHMgc28gd2UgY2FuIHNlZSB0aGVtIGJldHRlcg0KICAgICMgQ2hhbmdlIHNvbWUgb2YgdGhlIHRoZW1lIGFzcGVjdHMgbGlrZSB0ZXh0IHNpemUNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksDQogICAgICAgICAgIyBSb3RhdGUgdGhlIHgtYXhpcyB0aWNrIHRleHQgdG8gYmUgDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqV29yayBzbWFydGVyLCBub3QgaGFyZGVyOioqIFdoZW4gaXQgY29tZXMgdG8gY2VydGFpbiBjb21wbGV4IHZpc3VhbGl6YXRpb25zLCBpdCBpcyBiZXN0IHRvIGF2b2lkIHJlLWludmVudGluZyB0aGUgd2hlZWwuIFRoZXJlIGFyZSBzbyBtYW55IHBhY2thZ2VzIGF2YWlsYWJsZSB0aGF0IGFjY29tcGxpc2ggYSBncmVhdCBudW1iZXIgb2YgZGlmZmVyZW50IHZpc3VhbGl6YXRpb25zLiBUaGUga2V5IGlzIHRvIGtub3dpbmcgd2hhdCAqa2luZCogb2YgdmlzdWFsaXphdGlvbiB5b3Ugd2FudCB0byBwcm9kdWNlLCBhbmQgdGhlbiBjaGVja2luZyBpZiBhIHBhY2thZ2UgZXhpc3RzLiBXaGlsZSB0aGUgKipnZ3Bsb3QqKiBwYWNrYWdlIGNhbiBhY2NvbXBsaXNoIHF1aXRlIGEgZmV3IGJhc2ljIGFuZCBjb21wbGV4bHkgbGF5ZXJlZCB2aXN1YWxpemF0aW9ucywgb25lcyBsaWtlIHRoZSBhYm92ZSBzY2F0dGVycGxvdCBtYXRyaXggcmVxdWlyZSB0aGUgZ2VuZXJhdGlvbiBhbmQgcmVwbGFjZW1lbnQgb2YgbXVsdGlwbGUgcGxvdCB0eXBlcy4gVGhpcyByZXF1aXJlcyBnb2luZyB0byBhIGRlZXBlciBsZXZlbCBvZiBrbm93bGVkZ2UgYmV5b25kIHRoaXMgY291cnNlLiBBIG5pY2UgcGFja2FnZSBsaWtlICoqR0dhbGx5KiogdGFrZXMgYXdheSB0aG9zZSBpc3N1ZXMgd2hpbGUgb2ZmZXJpbmcgdGhlIGFiaWxpdHkgdG8gc3RpbGwgY3VzdG9taXplIGFuZCBhbHRlciB5b3VyIHBsb3QgZm9ybWF0IHRvIGEgY2VydGFpbiBleHRlbnQuDQo6OjoNCg0KIyMjIDEuMy40IEludGVycHJldGluZyBhIHNjYXR0ZXJwbG90IG1hdHJpeA0KDQpBcyB5b3UgY2FuIHNlZSwgdGhlIHNjYXR0ZXJwbG90IG1hdHJpeCBjYW4gcHJvdmlkZSBhIHF1aWNrIHdheSB0byBjaGVjayB0aGF0IHlvdXIgcmVwbGljYXRlcyBhcmUgc2ltaWxhciwgd2hpbGUgeW91ciBjb21wYXJpc29uIGNvbmRpdGlvbnMgc2hvdWxkIHNob3cgbW9yZSB2YXJpYXRpb24gYmV0d2VlbiB0aGVtLiBMb29raW5nIGF0IG91ciBhYm92ZSBwbG90LCB0aGUgdHJpYW5ndWxhciBzZWN0aW9ucyBvZiBzY2F0dGVycGxvdHMgaW4gdGhlIHRvcC1sZWZ0IGFuZCBsb3dlci1yaWdodCByZXByZXNlbnQgdGhlIGNvbXBhcmlzb24gb2YgcmVwbGljYXRlcyBpbiBvdXIgZXhwZXJpbWVudC4gVGhlc2Ugc2hvdWxkIHByb2R1Y2Ugc2NhdHRlciBwb2ludHMgdGhhdCBhcmUgY2xvc2UgdG8gdGhlIGRpYWdvbmFsIC0gbWVhbmluZyB0aGUgcmVwbGljYXRlcyBhcmUgcXVpdGUgc2ltaWxhciBpbiB0aGUgZGF0YSBwcm9kdWNlZC4NCg0KSW4gdGhlIGxvd2VyLWxlZnQgY29ybmVyIG9mIHRoZSBwbG90IHdlIGZpbmQgdGhlIGNvbXBhcmlzb24gYmV0d2VlbiBvdXIgdHdvIGNvbmRpdGlvbnMgKDI0LSB2cyA3Mi1ob3VyIGRhdGEpIGFuZCB5b3UgY2FuIHNlZSBtdWNoIG1vcmUgc2NhdHRlciBhd2F5IGZyb20gdGhlIGRpYWdvbmFsLiBJZiBvdXIgY29uZGl0aW9ucyByZWFsbHkgZG8gaW5kdWNlIGRpZmZlcmVudCB0cmFuc2NyaXB0aW9uYWwgcHJvZmlsZXMsIHRoaXMgaXMgd2hhdCB3ZSBzaG91bGQgZXhwZWN0ZWQgdG8gc2VlLg0KDQpBY3Jvc3MgdGhlIGRpYWdvbmFsIHdlIHNlZSB0aGUgZGVuc2l0eSBwbG90IG9mIG91ciByZWFkIGNvdW50cy4gVGhlc2UgYXJlbid0IHBhcnRpY3VsYXJseSBoZWxwZnVsIHdpdGggdGhpcyBkYXRhc2V0IGFzIHRoZSBkYXRhIHByZWRvbWluYW50bHkgYXBwZWFycyB0byBoYXZlIGxvdyByZWFkIGNvdW50cyBldmVuIGFmdGVyIHdlIGZpbHRlciBmb3IgYSBtaW5pbXVtIG9mIDEwIHJlYWRzIHBlciBnZW5lLg0KDQpUaGlzIHRlY2huaXF1ZSBpcyBhbHNvIGEgdXNlZnVsIHdheSB0byBpZGVudGlmeSBwb3RlbnRpYWxseSBtaXNsYWJlbGVkIHNhbXBsZXMgYmVmb3JlIHByb2Nlc3NpbmcgeW91ciBkYXRhIGZvciBhbmFseXNpcy4gQ2hhbmdlcyBmcm9tIHRoZSBhYm92ZSBwYXR0ZXJuIGNhbiBzaWduYWwgdGhlIG1pc2xhYmVsaW5nIG9mIHNhbXBsZXMgb3IgY2hhbmdlcyB0byB0aGUgcHJvY2VzcyBvZiBzYW1wbGUgcHJlcGFyYXRpb24uDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqTm90IGp1c3QgZm9yIGV4cHJlc3Npb24gYW5hbHlzaXMhKiogV2hpbGUgd2UgaGF2ZSB1c2UgdGhlIHNjYXR0ZXJwbG90IG1hdHJpY2VzIGluIHRoaXMgc2VjdGlvbiB0byBjb21wYXJlIG91ciBSTkFzZXEgZGF0YXNldHMsIHRoZXNlIHBsb3QgZm9ybWF0cyBoYXZlIGEgbXVjaCBicm9hZGVyIHVzZSBpbiBkZXRlcm1pbmluZyBpZiBhbnkgcG9zc2libGUgbGluZWFyIGNvcnJlbGF0aW9uIG1heSBleGlzdCBiZXR3ZWVuICpjb250aW51b3VzIHZhcmlhYmxlcyouIEl0J3MgYSBxdWljayB3YXkgdG8gbG9vayBhdCBhIHdpZGUgc2NvcGUgb2YgdmFyaWFibGVzIGZvciBwb3NzaWJsZSByZWxhdGlvbnNoaXBzIHdpdGhvdXQgZG9pbmcgYWxsIHRoZSBpbi1kZXB0aCBhbmFseXNpcyBmaXJzdC4gVGhpbmsgb2YgaXQgYXMgeWV0IGFub3RoZXIgaGVscGZ1bCB0b29sIGZvciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIQ0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjQuMCBXaGF0IGRvZXMgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZGF0YSBsb29rIGxpa2U/DQoNCkRpZ2dpbmcgZG93biBpbnRvIHlvdXIgREUgcmVzdWx0cywgd2l0aCB0aGUgaHVtYW4gZ2Vub21lLCB5b3UgYXJlIGxvb2tpbmcgdG8gaWRlbnRpZnkgdHJlbmRzIGluIGdlbmUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBhY3Jvc3MgNDMsMDAwIHBvdGVudGlhbCB0cmFuc2NyaXB0cyBzZXF1ZW5jZXMgLSBvbmx5IGhhbGYgb2Ygd2hpY2ggcHJvZHVjZSBwcm90ZWlucy4gWW91IGNhbiBpbWFnaW5lIHRoYXQgb3BlbmluZyB1cCBhIHRhYnVsYXIgZmlsZSBvZiB0aGF0IHNpemUgY291bGQgdGFrZSBhIGJpdCBvZiB0aW1lLg0KDQpGb3IgZWFjaCBERSBleHBlcmltZW50IHRoZXJlIGFyZSBhIG51bWJlciBvZiB2YWx1ZXMgZ2VuZXJhdGVkLiBJbiBhIHBvcHVsYXIgcGFja2FnZSBsaWtlIGBERVNlcTJgIHlvdSB3aWxsIHR5cGljYWxseSBmaW5kOg0KDQp8IFZhcmlhYmxlICAgICAgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IEdlbmUgbmFtZSAgICAgICAgICAgICB8IFRoZSBuYW1lcyBvZiB5b3VyIGdlbmVzIHdoaWNoIG1heSBiZSBpbiBkaWZmZXJlbnQgZm9ybWF0cyBsaWtlIEVudHJleiBJRCwgRW5zZW1ibCwgb3IgZ2VuZSBzeW1ib2wgICAgICAgICAgICAgICAgfA0KfCBCYXNlIG1lYW4gICAgICAgICAgICAgfCBBdmVyYWdlIG9mIHRoZSBub3JtYWxpemVkIGNvdW50cyB2YWx1ZXMsIGFjY291bnRpbmcgZm9yIHNpemUgZmFjdG9ycywgdGFrZW4gb3ZlciBhbGwgc2FtcGxlcyAoYW5kIG9yIHJlcGxpY2F0ZXMpIHwNCnwgTG9nJF97Mn0kIGZvbGQtY2hhbmdlIHwgVGhlIGVmZmVjdCBzaXplIGVzdGltYXRlIC0gaG93IG11Y2ggeW91ciBnZW5lJ3MgZXhwcmVzc2lvbiBoYXMgY2hhbmdlZCBmcm9tIGNvbnRyb2wvYmFzZSBjb25kaXRpb25zICAgICAgICAgICAgICB8DQp8IExvZyBmb2xkLWNoYW5nZSBTRSAgICB8IEFuIGVzdGltYXRlIG9mIGVmZmVjdCBzaXplIHVuY2VydGFpbnR5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwLXZhbHVlICAgICAgICAgICAgICAgfCBIeXBvdGhlc2lzIHRlc3QgYWdhaW5zdCBIJF97MH0kIHRoYXQgbm8gZGlmZmVyZW5jZSBleGlzdHMgYmV0d2VlbiBzYW1wbGUgZ3JvdXBzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYWRqdXN0ZWQgcC12YWx1ZSAgICAgIHwgQW4gYWRqdXN0ZWQgcC12YWx1ZSBhZnRlciB0YWtpbmcgaW50byBhY2NvdW50IG11bHRpcGxlIHRlc3RpbmcgYmV0d2VlbiBncm91cHMvc2FtcGxlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQoNCllvdSBjYW4gZmluZCBtb3JlIGluZm9ybWF0aW9uIG9uIHdvcmtpbmcgd2l0aCB0aGUgYERFU2VxMmAgcGFja2FnZSBbaGVyZV0oaHR0cHM6Ly9iaW9jLmlzbS5hYy5qcC9wYWNrYWdlcy8yLjE0L2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9iZWdpbm5lci5wZGYpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjUuMCBZb3UgY2FuIGV4YW1pbmUgZGF0YSBvbiBtYW55IHNjYWxlcw0KDQpPbmNlIHdlIGhhdmUgYSBzZXQgb2YgZGF0YSB3ZSBjYW4gZXhhbWluZSBpdCBmcm9tIG1hbnkgYXNwZWN0czoNCg0KMS4gIENvbXBhcmUgYWxsIGdlbmVzIHdpdGhpbiBhIHNhbXBsZSBhbmQgYWNyb3NzIHNhbXBsZXMNCjIuICBDb21wYXJlIERFIGluIGEgc3Vic2V0IG9mIGdlbmVzIHdpdGhpbiBhIHNhbXBsZSBhbmQgYWNyb3NzIHNhbXBsZXMNCjMuICBDb21wYXJlIERFIHJlc3VsdHMgYmFzZWQgb24gZ2VuZSBmdW5jdGlvbg0KDQpMZXQncyBleHBsb3JlIHRoZXNlIGRpZmZlcmVudCBhc3BlY3RzIGFuZCB0aGUga2luZCBvZiBwbG90cyB3ZSBjYW4gZ2VuZXJhdGUgdG8gYWNjb21vZGF0ZSB0aGUgc2l6ZSBvZiBvdXIgZGF0YSBzZXQuIEZpcnN0LCBob3dldmVyLCB3ZSBuZWVkIHRvIGZpbmQgYSB1c2VmdWwgZGF0YSBzZXQgdG8gd29yayB3aXRoLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNS4xIFVzZSB0aGUgYHJlYWR4bGAgbGlicmFyeSB0byBvcGVuIHlvdXIgLnhsc3ggZmlsZXMNCg0KV2UndmUgYnJpZWZseSB0b3VjaGVkIG9uIHVzaW5nIHRoaXMgcGFja2FnZSBpbiBgTGVjdHVyZSAwMWAgdG8gaGVscCByZWFkIG91ciBNaWNyb3NvZnQgRXhjZWwtYmFzZWQgZGF0YS4gVG9kYXkncyBkYXRhIHNldCBmcm9tIEJsYW5jLU1lbG8gZXQgYWwuIChDZWxsIDIwMjApLCBjb21lcyB0byB1cyBpbiB0aGUgZm9ybSBvZiBhbiBleGNlbCBmaWxlLiBMZXQncyB1c2Ugc29tZSBvZiB0aGUgdG9vbHMgZnJvbSB0aGlzIHBhY2thZ2UgdG8gaGVscCBvcGVuIHVwIG91ciBkYXRhIGZpbGUuIFdlJ2xsIHN0YXJ0IGJ5IHBlZWtpbmcgYXQgaG93IG1hbnkgc2hlZXRzIHRoZXJlIGFyZSB1c2luZyBgZXhjZWxfc2hlZXRzKClgLg0KDQpgYGB7cn0NCiMgV2hhdCBhcmUgdGhlIHNoZWV0cyB0byBvcGVuIGluIG91ciBkYXRhIHNldD8NCmV4Y2VsX3NoZWV0cyguLi4pDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjUuMiBPcGVuIGVhY2ggc2hlZXQgc2VwYXJhdGVseSB3aXRoIGByZWFkX2V4Y2VsKClgDQoNCk5vdyB0aGF0IHdlIGNhbiBzZWUgdGhlcmUgYXJlIHR3byBzaGVldHMgaW4gb3VyIGRhdGEsIHdlIGNhbiBhc3NpZ24gZWFjaCBvbmUgdG8gYSBkaWZmZXJlbnQgZGF0YSBmcmFtZSB1c2luZyB0aGUgYHJlYWRfZXhjZWwoKWAgY29tbWFuZCB3aGljaCBjb250YWlucyB0aGUgcGFyYW1ldGVyIGBzaGVldGAuIFdlIGNhbiB1c2UgdGhpcyB0byBkZXRlcm1pbmUgd2hpY2ggc2hlZXQgdG8gbG9hZCBlaXRoZXIgYmFzZWQgb24gaXQncyBwb3NpdGlvbiAoaW50ZWdlcikgb3IgaXQncyBuYW1lIGFzIHNwZWNpZmllZCBieSBgZXhjZWxfc2hlZXRzKClgLg0KDQpgYGB7cn0NCiMgQXNzaWduIG91ciBsZWdlbmQgDQpibGFuY29fbGVnZW5kLmRmIDwtIHJlYWRfZXhjZWwoIi4vZGF0YS9CbGFuY28tTWVsbzIwMjBDZWxsLlN1cHAxLnhsc3giLCBzaGVldD0uLi4pDQoNCiMgQXNzaWduIG91ciBkYXRhDQpibGFuY29fZGF0YS5kZiA8LSByZWFkX2V4Y2VsKCIuL2RhdGEvQmxhbmNvLU1lbG8yMDIwQ2VsbC5TdXBwMS54bHN4Iiwgc2hlZXQ9Li4uKQ0KDQpzdHIoYmxhbmNvX2xlZ2VuZC5kZikNCnN0cihibGFuY29fZGF0YS5kZikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNS4zIEFCVzogQWx3YXlzIEJlIFdyYW5nbGluZyAoeW91ciBkYXRhKQ0KDQpZZXMsIGl0IGxvb2tzIGxpa2UgdGhlIGxlZ2VuZCBoYXMgc29tZSBoZWxwZnVsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhc2V0IGl0c2VsZiBmb3VuZCBpbiB0aGUgc2Vjb25kIHNoZWV0LiBUaGUgREUgZGF0YSBsb29rcyBsaWtlIGl0J3Mgc3BsaXQgYWNyb3NzIDIwIGNvbHVtbnMgZW5jb21wYXNzaW5nIDEwIGV4cGVyaW1lbnRhbCBkYXRhIHNldHMuIEZvciBlYWNoIGRhdGEgc2V0LCBpdCBsb29rcyBsaWtlIHRoZXJlIGlzIGEgTG9nJF97Mn0kIGZvbGQtY2hhbmdlIHZhbHVlICgqKkwyRkMqKikgYW5kIGFuIGFkanVzdGVkIHAtdmFsdWUgKCoqcGFkaioqKS4NCg0KTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rIGF0IGBibGFuY29fbGVnZW5kLmRmYCBmaXJzdC4gRnJvbSBpdCB3ZSBjYW4gcGFyc2Ugb3V0IHNvbWUgaW1wb3J0YW50IGluZm9ybWF0aW9uIGFib3V0IHRoZSBleHBlcmltZW50cyB0aGVtc2VsdmVzIGxpa2UgdGhlICoqcGF0aG9nZW4qKiBhbmQgKipob3N0KiogaW4gZWFjaCBleHBlcmltZW50YWwgc2V0Lg0KDQpgYGB7cn0NCiMgTG9vayBhdCB0aGUgZW50aXJlIGxlZ2VuZA0KYmxhbmNvX2xlZ2VuZC5kZg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS41LjQgRXh0cmFjdCBzdWJzdHJpbmcgcGF0dGVybnMgYW5kIHNhdmUgdGhlbSB1c2luZyBgc3RyX21hdGNoX2FsbCgpYA0KDQpZb3UgbWF5IHJlbWVtYmVyIHRoYXQgd2UgY2FuIGVhc2lseSBtYXRjaCBmb3IgcGF0dGVybnMgaW4gb3VyIHN0cmluZyB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgYHN0cmluZ3JgIHBhY2thZ2UgYnV0IHdpdGggYSBmdW5jdGlvbiBsaWtlIGBzdHJfbWF0Y2hfYWxsKClgIHlvdSBjYW4gYWxzbyBpbmNsdWRlIG1hdGNoaW5nICpncm91cHMqIHRvIHlvdXIgcGF0dGVybiB0aGF0IGNhbiBiZSBjYXB0dXJlZC4gRm9yICplYWNoKiBzdHJpbmcgbWF0Y2gsIHRoaXMgZnVuY3Rpb24gd2lsbCByZXR1cm4gYSBtYXRyaXggdGhhdCBjb250YWlucyB0aGUgY29tcGxldGUgbWF0Y2hpbmcgcGF0dGVybiBhbmQgYW55IGdyb3VwZWQgcGF0dGVybnMgdGhhdCBhcmUgZGVub3RlZCBieSB0aGUgYCgpYCBwYXJlbnRoZXNlcy4NCg0KKklmKiBhIHN0cmluZyBjb250YWlucyBtdWx0aXBsZSBtYXRjaGVzIHRvIHlvdXIgcGF0dGVybiwgaXQgd2lsbCBnZW5lcmF0ZSBhZGRpdGlvbmFsIHJvd3MgaW4gdGhlIG1hdHJpeCBmb3IgdGhhdCBzdHJpbmcuDQoNCkxvb2tpbmcgYXQgb3VyIGBOb3Rlc2AgY29sdW1uIG9mIGBibGFuY29fbGVnZW5kLmRmYCwgaXQgbG9va3MgbGlrZSBpdCBmb2xsb3dzIGEgcmVndWxhciBwYXR0ZXJuIHdlcmUgd2UgY2FuIGV4dHJhY3QgdGhlIHBhdGhvZ2VuIGFuZCBob3N0IGNlbGwgaW5mb3JtYXRpb24NCg0KfCBzZWN0aW9uICAgICAgICAgICAgICB8IEV4YW1wbGVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBQYXRob2dlbiBuYW1lICAgICAgICB8IFNBUlMtQ29WLTIsIFJTViAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBJbnRlcm1lZGlhdGUgc3RyaW5ncyB8IGluZmVjdGlvbiwgaW5mZWN0aW9uIHdpdGggUnV4b2xpdGluaWIgfA0KfCBIb3N0IGNlbGwgdHlwZSAgICAgICB8IEE1NDksIEE1NDktQUNFMiAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpgYGB7cn0NCiMgcGF0dGVybiBtYXRjaCBmb3IgcGF0aG9nZW4gYW5kIGhvc3QgaW5mb3JtYXRpb24gYW5kIHNhdmUgaXQgaW50byBhIGRhdGFmcmFtIHlvdSBjYW4gYWNjZXNzIGxhdGVyLg0KDQpleHBfaW5mbyA8LQ0KICBibGFuY29fbGVnZW5kLmRmICU+JSANCiAgDQogICMgVXNlIGRwbHlyOjpzZWxlY3QgYmVjYXVzZSB0aGUgc2VsZWN0IGZ1bmN0aW9uIGhhcyBiZWVuIG92ZXJyaWRkZW4gYnkgYW5vdGhlciBsaWJyYXJ5DQogIGRwbHlyOjpzZWxlY3QoTm90ZXMpICU+JSANCiAgDQogICMgVXNlIHN0cl9tYXRjaF9hbGwgdG8gZ3JhYiB0aGUgcGF0dGVybiB3ZSB3YW50LCBwaWVjZSBieSBwaWVjZQ0KICAjIGV4YW1wbGUgc3RyaW5nOiBTQVJTLUNvVi0yIGluZmVjdGlvbiAoQTU0OS1BQ0UyIGNlbGxzLCBNT0k6IDIsIDI0aHBpKQ0KICBzdHJfbWF0Y2hfYWxsKHBhdHRlcm4gPSByIiguLi4pIikgJT4lIA0KICANCiAgIyBTZXQgaW50byBhIGRhdGEgZnJhbWUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgDQogICMgQ2hhbmdlIHRoZSBjb2x1bW4gbmFtZXMgKHdvdWxkIGJlIFgxLCBYMiwgWDMgb3RoZXJ3aXNlKQ0KICBgLi4uYChjKCJub3RlcyIsICJwYXRob2dlbiIsICJob3N0IikpDQogIA0KICAjIEFsdGVybmF0aXZlIGNvZGUgdG8gc2V0IG5hbWVzOiANCiAgIyBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoIm5vdGVzIiwgInBhdGhvZ2VuIiwgImhvc3QiKSkNCg0KIyBUYWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0aW5nIGRhdGENCmV4cF9pbmZvDQpgYGANCg0KYGBge3J9DQojIEFkZCB0aGlzIG5ldyBpbmZvcm1hdGlvbiB0byBvdXIgb3JpZ2luYWwgbGVnZW5kIGluZm9ybWF0aW9uDQoNCmJsYW5jb19leHBfaW5mby5kZiA8LSANCiAgYmxhbmNvX2xlZ2VuZC5kZiAlPiUgDQogIA0KICAjIE1ha2Ugc29tZSBuZXcgY29sdW1ucyB3aXRoIHBhdGhvZ2VuIGFuZCBob3N0IGluZm9ybWF0aW9uDQogIG11dGF0ZShwYXRob2dlbiA9IGV4cF9pbmZvJHBhdGhvZ2VuLCAjIE91ciBwYXRob2dlbiBpbmZvcm1hdGlvbg0KICAgICAgICAgaG9zdCA9IGV4cF9pbmZvJGhvc3QpICU+JSAjIE91ciBob3N0IGluZm9ybWF0aW9uIA0KICANCiAgIyBDb21iaW5lIG91ciBwYXRob2dlbiBhbmQgaG9zdCBpbmZvcm1hdGlvbiBpbnRvIGEgc2luZ2xlIGNvbHVtbiBhcyB3ZWxsDQogIHVuaXRlKGNvbCA9IC4uLiwgLi4uLCBzZXA9Il8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICANCiAgIyBKdXN0IGtlZXAgdGhlIG9yaWdpbmFsIEZpZWxkIGNvbHVtbiBhbmQgdGhlIG5ldyBvbmVzDQogIGRwbHlyOjpzZWxlY3QoMSwgNCwgNSwgNikgDQoNCiMgVGFrZSBhIGxvb2sgYXQgd2hhdCB3ZSBhcmUgd29ya2luZyB3aXRoDQpibGFuY29fZXhwX2luZm8uZGYNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNS41IENvbnZlcnQgb3VyIERFIGRhdGEgdGFibGUgaW50byBsb25nLWZvcm1hdCBhbmQgam9pbiB3aXRoIG91ciBjZWxsIGluZm9ybWF0aW9uDQoNCk5vdyB0aGF0IHdlIGhhdmUgY2F0YWxvZ3VlZCBvdXIgZXhwZXJpbWVudGFsIGluZm9ybWF0aW9uLCBsZXQncyBidWlsZCB0aGF0IGludG8gYSBsb25nLWZvcm1hdCB2ZXJzaW9uIG9mIG91ciBhY3R1YWwgREUgZGF0YXNldCBmb3VuZCBpbiBgYmxhbmNvX2RhdGEuZGZgLiBUaGUgc3RlcHMgd2UnbGwgdGFrZSB0byBwcmVwYXJlIG91ciBkYXRhIGFyZQ0KDQoxLiAgQ29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQgd2l0aCBgcGl2b3RfbG9uZ2VyKClgLg0KDQoyLiAgSm9pbiB3aXRoIG91ciBleHBlcmltZW50YWwgZGF0YSBpbmZvcm1hdGlvbiBpbiBgYmxhbmNvX2V4cF9pbmZvLmRmYA0KDQozLiAgUmVwbGFjZSBvdXIgZXhwZXJpbWVudCBpbmZvcm1hdGlvbiAoZm9ybWVybHkgY29sdW1uIGluZm9ybWF0aW9uKSB0byBhIGNvbnNpc3RlbnQgZm9ybWF0IG9mIGBleHBlcmltZW50X3ZhbHVlYCBzbyB3ZSBjYW4gc3BsaXQgaXQgb3V0IHByb3Blcmx5Lg0KDQo0LiAgU2VwYXJhdGUgb3VyIGBleHBlcmltZW50X3ZhbHVlYCBpbmZvcm1hdGlvbi4gQ3VycmVudGx5IHdlJ2xsIGhhdmUgZXhwZXJpbWVudHMgbWFwcGluZyB0byBib3RoIGFuIEwyRkMgYW5kIHBhZGogdmFsdWUNCg0KNS4gIENvbnZlcnQgb3VyIGRhdGEgYmFjayBvdXQgYSBsaXR0bGUgYml0IHRvIGEgc2xpZ2h0bHkgd2lkZXIgZm9ybWF0IHdpdGggYHBpdm90X3dpZGVyKClgDQoNCmBgYHtyfQ0KYmxhbmNvX2RhdGFfbG9uZy5kZiA8LQ0KICBibGFuY29fZGF0YS5kZiAlPiUgDQogIA0KICAjIGNvbGxhcHNlIGFsbCBvZiB0aGUgZGF0YSBjb2x1bW5zDQogIHBpdm90X2xvbmdlcihjb2xzPSgyOjIxKSwgbmFtZXNfdG8gPSAiZXhwZXJpbWVudCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiAgDQogICMgYWRkIG91ciBleHBlcmltZW50YWwgaW5mbyB0byBlYWNoIG9ic2VydmF0aW9uLiBOb3RlIHRoYXQgZWFjaCBnZW5lIGhhcyBtdWx0aXBsZSBvYnMgbm93Lg0KICBmdWxsX2pvaW4oLiwgYmxhbmNvX2V4cF9pbmZvLmRmLCBieT1jKCJleHBlcmltZW50IiA9IC4uLikpICU+JSANCiAgDQogICMgV2UgbmVlZCB0byBmaXggYWxsIG9mIHRoZSAicGFkal9leHBlcmltZW50IiB2YWx1ZXMgdG8gImV4cGVyaW1lbnRfcGFkaiIgZm9ybWF0DQogICMgTm90ZSB3ZSBjb3VsZCBoYXZlIHJlbmFtZWQgb3VyIGNvbHVtbnMgQkVGT1JFIHBpdm90aW5nDQogIG11dGF0ZShleHBlcmltZW50ID0gc3RyX3JlcGxhY2VfYWxsKC4kZXhwZXJpbWVudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49ciIoLi4uKSIsICMgQ2FwdHVyZSB0aGUgZ3JvdXBzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHV0IHRoZW0gYmFjayB0b2dldGhlciBpbiBhIHN3aXRjaGVkIG9yZGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gciIoLi4uKSIpKSAlPiUgICAgDQogIA0KICAjIE5vdyB3ZSB3YW50IHRvIHNwbGl0IHRoZSBleHBlcmltZW50IGNvbHVtbiBpbnRvIHR3bzogZXhwZXJpbWVudCBhbmQgZGF0YV90eXBlIChMMkZDIG9yIHBhZGopDQogIHNlcGFyYXRlKGV4cGVyaW1lbnQsIGMoImV4cGVyaW1lbnQiLCAiZGF0YV90eXBlIiksIHNlcD0iXyIsIHJlbW92ZT1UUlVFKSAlPiUgDQogIA0KICAjIFBpdm90IG91dCB0aGUgZGF0YSBzbyB0aGF0IGVhY2ggb2JzZXJ2YXRpb24gZm9yIGVhY2ggZXhwZXJpbWVudCBoYXMgYW4gTDJGQyBhbmQgcGFkaiB2YWx1ZQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZGF0YV90eXBlLCB2YWx1ZXNfZnJvbSA9IHZhbHVlKQ0KDQpibGFuY29fZGF0YV9sb25nLmRmDQpgYGANCg0KYGBge3J9DQojIERvbid0IGZvcmdldCB0byBzYXZlIHlvdXIgd3JhbmdsZWQgZGF0YSENCnNhdmUoYmxhbmNvX2RhdGFfbG9uZy5kZiwgZmlsZT0iLi9kYXRhL0xlY3R1cmUwNF9ibGFuY28uUkRhdGEiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuMC4wIFZpc3VhbCBpbnNwZWN0aW9uIG9mIERFIGFjcm9zcyBhIHNhbXBsZQ0KDQpBZnRlciBhbGwgb2Ygb3VyIGRhdGEgd3JhbmdsaW5nLCB5b3UgY2FuIHNlZSB3ZSBoYXZlIGEgZGF0YSBmcmFtZSB3aXRoIG5lYXJseSAxLzQgbWlsbGlvbiBvYnNlcnZhdGlvbnMuIFRoaXMgc3BhbnMgYWNyb3NzIDEwIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLiBGcm9tIGEgYnJvYWQgbGV2ZWwsIHdlIGNhbiBsb29rIGFjcm9zcyBlbnRpcmUgZGF0YSBzZXRzIHRvIGlkZW50aWZ5IGdlbmVzIG9mIGludGVyZXN0LiBUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyB0byBkbyB0aGlzIGFuZCB3ZSdsbCBiZWdpbiB3aXRoIGEgc3RhbmRhcmQgc2V0IG9mIHBsb3RzIHRoYXQgY2FuIGNvbnZleSB0aGUgb3ZlcmFsbCBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGEuIFdoaWxlIG91ciB2aWV3IG9mIHRoZSBkYXRhIGlzIGxvdy1yZXNvbHV0aW9uIHdlIGFyZSBzdGlsbCBhYmxlIHRvIGNvbXBhcmUgc3BlY2lmaWMgZ2VuZSBpbmZvcm1hdGlvbiBjZW50ZXJlZCBhcm91bmQgbWVhbiBleHByZXNzaW9uIGxldmVscywgbWFnbml0dWRlIG9mIGNvbXBhcmlzb24sIGFuZCBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBUaGUgTUEgcGxvdCBkZXNjcmliZXMgdGhlIHNwcmVhZCBvZiBvdXIgREUgZGF0YQ0KDQpVc2VkIG9yaWdpbmFsbHkgaW4gdGhlIGFwcGxpY2F0aW9uIG9mIE1pY3JvYXJyYXkgZGF0YSBhbmFseXNpcywgdGhlc2UgcGxvdHMgYXJlIGFsc28gYXBwbGllZCB0byB2aXN1YWxpemluZyBoaWdoLXRocm91Z2hwdXQgc2VxdWVuY2luZyBhbmFseXNpcy4NCg0KVGhpcyBpcyBhIGRhdGEgdHJhbnNmb3JtYXRpb24gYmV0d2VlbiB0d28gc2V0cyBvZiBkYXRhIHN1Y2ggdGhhdA0KDQotICAgYE1gIChtaW51cyBpbiB0aGUgbG9nIHNjYWxlKSByZXByZXNlbnRzIHRoZSBsb2ckX3syfSQgcmF0aW8gb2YgZm9sZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHlvdXIgZGF0YSBzZXRzDQoNCi0gICBgQWAgKGF2ZXJhZ2UgaW4gdGhlIGxvZyBzY2FsZSkgcmVwcmVzZW50cyB0aGUgYXZlcmFnZSBleHByZXNzaW9uIHNpZ25hbA0KDQpXZSBwbG90IG91ciBgTWAgdmFsdWVzIGFsb25nIHRoZSB5LWF4aXMgYWdhaW5zdCB0aGUgY29ycmVzcG9uZGluZyBgQWAgdmFsdWVzIG9uIHRoZSB4LWF4aXMuIERvIHRoZXNlIGNoYXJhY3RlcmlzdGljcyBzb3VuZCBmYW1pbGlhcj8gVGhleSBhcmUgc3RhbmRhcmQgb3V0cHV0IGZvciBgREVTZXEyYCBkYXRhIHNldHMhIFVuZm9ydHVuYXRlbHkgb3VyIGFib3ZlIGRhdGEgZG9lc24ndCBoYXZlIHRoYXQgaW5mb3JtYXRpb24gYW55bW9yZSwgYnV0IHdlIGhhdmUgYSBkYXRhc2V0IHRoYXQgZG9lcyEgSXQncyBhbm90aGVyIGRhdGFzZXQgZnJvbSBhbiBlYXJsaWVyIHByZS1wdWJsaWNhdGlvbiBjb3B5IG9mIHRoZSBCbGFuY28tTWVsbyBldCBhbC4sIDIwMjAgcGFwZXI6IGBCbGFuY28tTWVsbzIwMjBfU3VwcF9EYXRhXzQueGxzeGAuDQoNCkxldCdzIG9wZW4gdGhpcyB1cCBhbmQgdGFrZSBhIGxvb2shDQoNCmBgYHtyfQ0KIyBXZSdsbCBiZSBsb29raW5nIGF0IERFIGRhdGEgZm9yIElBViBpbmZlY3Rpb24gb2YgQTU0OSBjZWxscw0KREVfQTU0OV9JQVYuZGYgPC0gcmVhZF9leGNlbCguLi4sIHNoZWV0PTIpDQoNCiMgTG9vayBhdCB0aGUgZGF0YSBmcmFtZSB3ZSd2ZSBtYWRlDQpoZWFkKERFX0E1NDlfSUFWLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4xLjEgQUJtVzogQSBCaXQgbW9yZSBXcmFuZ2xpbmcgd2l0aCBgYWNyb3NzKClgDQoNCk9rYXksIHNvIHdlIGNhbid0IHVzZSB0aGUgZGF0YSAqcmlnaHQqIGF3YXkuIFRoZSBgcmVhZF9leGNlbCgpYCBjb21tYW5kIGhhcyBkZWNpZGVkIHRvIGNvbnZlcnQgc29tZSBvZiBvdXIgY29sdW1ucyB0byBjaGFyYWN0ZXIgZm9ybWF0IGJlY2F1c2UgaXQgZGV0ZWN0ZWQgYE5hTmAgdmFsdWVzLiBTbyB3ZSdsbCBoYXZlIHRvIHF1aWNrbHkgY29lcmNlIHRoZSBkYXRhIGJlZm9yZSBtb3Zpbmcgb24uIFdlIGNhbiBxdWlja2x5IGNvbnZlcnQgdGhlIGRhdGEgd2l0aCBhIGBtdXRhdGUoKWAgY29tbWFuZCB1c2luZyB0aGUgaGVscGVyIHZlcmIgYGFjcm9zcygpYC4NCg0KVG8gc3VjY2Vzc2Z1bGx5IHVzZSB0aGlzIGhlbHBlciwgd2Ugd2lsbCB3YW50IHRvIGxpc3Qgb3V0IHRoZSBjb2x1bW5zIHdlIHdhbnQgdG8gdXNlIGluIHRoZSBgLmNvbHNgIHBhcmFtZXRlciwgYW5kIHRoZW4gYXBwbHkgYSBmdW5jdGlvbiBsaWtlIGBhcy5udW1lcmljYC4gV2UgZG9uJ3QgYWN0dWFsbHkgbmVlZCAqYWxsKiBvZiB0aGVzZSBjb2x1bW5zLCBidXQgaXQncyBnb29kIHByYWN0aWNlIQ0KDQpgYGB7cn0NCiMgQ29udmVydCBvdXIgbnVtZXJpYyBjb2x1bW5zIHRvIHRoZSBwcm9wZXIgdHlwZS4NCkRFX0E1NDlfSUFWLmRmIDwtIA0KICBERV9BNTQ5X0lBVi5kZiAlPiUNCiAgDQogICMgVXNlIG11dGF0ZSgpIHdpdGggYWNyb3NzKCkgdG8gY29udmVydCBjb2x1bW5zIHRvIG51bWVyaWMNCiAgbXV0YXRlKC4uLikNCg0KIyBDaGVjayB0aGUgcmVzdWx0DQpzdHIoREVfQTU0OV9JQVYuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqU2tpcCB0aGUgd3JhbmdsaW5nIHdpdGggYSBwcm9wZXIgaW1wb3J0ISoqIFdoaWxlIHdlIHVzZWQgdGhlIGFib3ZlIGltcG9ydCBhcyBhbiBvcHBvcnR1bml0eSB0byBwcmFjdGljZSBvdXIgd3JhbmdsaW5nIHNraWxscywgd2UgY291bGQgaGF2ZSBhdm9pZGVkIGl0IGFsdG9nZXRoZXIgaWYgd2UgaW1wb3J0ZWQgcHJvcGVybHkuIFdpdGhpbiB0aGUgKipyZWFkX2V4Y2VsKCkqKiBmdW5jdGlvbiB0aGVyZSBpcyBhIHBhcmFtZXRlciBjYWxsZWQgKioibmEiKiogd2hpY2ggYWxsb3dzIHVzIHRvIGRlZmluZSB0aGUgY2hhcmFjdGVyKHMpIHRoYXQgcmVwcmVzZW50IG51bGwgb3IgTkEgdmFsdWVzLiBUaGUgZGVmYXVsdCBpcyBzaW1wbHkgYmxhbmsgb3IgZW1wdHkgY2VsbHMgYnV0IGtub3dpbmcgd2hhdCB3ZSBkbyBub3cgYWJvdXQgdGhlIGRhdGFmcmFtZSwgd2UgY291bGQgYWxzbyBoYXZlIHVzZWQgc2V0IHRoZSBwYXJhbWV0ZXI6ICoqbmEgPSAiTmFOIioqIHdoaWNoIHdvdWxkIGFsbG93IG91ciBkYXRhZnJhbWUgdG8gcHJvcGVybHkgaW1wb3J0IHRoZSB2YWx1ZXMgYW5kIGNvcnJlY3RseSBhc3NpZ24gdmFyaWFibGUgdHlwZXMuIFNvLCBpdCdzIGFsd2F5cyBoZWxwZnVsIHRvIFtrbm93IHdoYXQgeW91ciBmdW5jdGlvbnMgY2FuIGRvIV0oJTVCaHR0cHM6Ly9yZWFkeGwudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvcmVhZF9leGNlbC5odG1sJTVEKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3JlYWRfZXhjZWwuaHRtbCkpDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjEuMiBCdWlsZCB5b3VyIE1BIHBsb3QgdXNpbmcgYGdncGxvdDJgDQoNCkdpdmVuIHRoYXQgd2UgYWxyZWFkeSBoYXZlIG91ciBkYXRhIGluIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZm9ybWF0LCB3ZSBjYW4ganVzdCB1c2UgYGdncGxvdDJgIHRvIHBsb3QgdGhlIGNvcnJlY3QgdmFyaWFibGVzIHRvIHRoZSB4IGFuZCB5IGF4ZXMuIEluIHRoaXMgY2FzZSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gdXNpbmcgdGhlIGBsb2cyRm9sZENoYW5nZWAgYXMgb3VyIGBNYCBhbmQgYGJhc2VNZWFuYCByZXByZXNlbnRzIG91ciBgQWAuIFRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHdlIGNhbiBjb2xvdXIgdGhpcyBwbG90IGJ1dCB3ZSdsbCB0YWtlIGludG8gYWNjb3VudCBvdXIgYHBhZGpgIHZhbHVlcyB0byBoaWdobGlnaHQgdGhvc2UgZ2VuZXMgd2l0aCBERSB2YWx1ZXMgdGhhdCBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCg0KTm93LCBpZiB3ZSB3ZXJlIHVzaW5nIHJhdyBvciBub3JtYWxpemVkIGV4cHJlc3Npb24gZGF0YSwgd2Ugd291bGQgbmVlZCB0byBjYWxjdWxhdGUgdGhlIHByb3BlciB2YWx1ZXMgZm9yIHRoZSB0cmFuc2Zvcm1hdGlvbiBvZiBgTWAgYW5kIGBBYC4gVGhlcmUgYXJlIGEgZmV3IHBhY2thZ2VzIHRoYXQgY2FuIHRha2UgY2FyZSBvZiB0aGlzIGZvciB5b3Ugc3VjaCBhcyBgZ2dtYXBsb3RgIHdoaWNoIHlvdSBjYW4gZmluZCBbaGVyZV0oaHR0cHM6Ly9ycGtncy5kYXRhbm92aWEuY29tL2dncHVici9yZWZlcmVuY2UvZ2dtYXBsb3QuaHRtbCkNCg0KRm9yIG91ciBwbG90LCB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gb3VyIHBvaW50cywgbGV0J3MgdXNlIHRoZSBgZ2doaWdobGlnaHRgIHBhY2thZ2UgZnJvbSBsYXN0IHdlZWshIFdoYXQgd2lsbCBvdXIgcHJlZGljYXRlcyBiZSBiYXNlZCBvbj8gTG9nfjJ+IGZvbGQgY2hhbmdlPyBBZGp1c3RlZCBwLXZhbHVlPw0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIE1BIHBsb3QhIE5lZWQgdG8gc2VsZWN0IGJhc2VkIG9uIGEgZm9sZCBjaGFuZ2UgPiB4IGFuZCBwYWRqIDw9IGZkcg0KIyBVc3VhbGx5IEZDID0gMS41LCBmZHIgPSAwLjA1IGJ1dCB3ZSdsbCB1c2UgdGhlICJzdGF0dXMiIHZhcmlhYmxlIGhlcmUgaW5zdGVhZC4NCg0KREVfQTU0OV9JQVYuZGYgJT4lIA0KICBmaWx0ZXIoc3RhdHVzID09ICJPSyIpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1iYXNlTWVhbiwgeSA9IGxvZzJGb2xkQ2hhbmdlKSArDQogIA0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogIA0KICAgICMgMy4gU2NhbGluZw0KICAgICMgVXNlIGEgbG9nMTAgc2NhbGUgb24gdGhlIHggYXhpcw0KICAgIHNjYWxlX3hfbG9nMTAoKSArIA0KICANCiAgICAjIFNldCB0aGUgYnJlYWtzIG9uIG91ciB5LWF4aXMgDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEwLCAxMCksIGJyZWFrcyA9IHNlcSgtMTAsIDEwLCAyKSkgKw0KICANCiAgICAjIDQuIEdlb21zDQogICAgIyBDb2xvdXIgb3VyIHBvaW50cyBhbGwgaW4gcmVkDQogICAgZ2VvbV9wb2ludChjb2xvdXIgPSAicmVkIiwgYWxwaGEgPSAwLjIsIHNpemU9NCwgbmEucm09VFJVRSkgKw0KICANCiAgICAjIyMgMi4xLjINCiAgICAjIENoYW5nZSBpdCBzbyB0aGF0IG9ubHkgaGlnaCBMMkZDIHdpdGggbG93IHAtdmFsdWVzIHdpbGwgYmUgaW4gcmVkLCB0aGUgcmVzdCBhcyBibGFjaw0KICAgIGdnaGlnaGxpZ2h0KC4uLiwgLi4uLCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImJsYWNrIikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjEuMyBJbnRlcnByZXRpbmcgb3VyIE1BIHBsb3QNCg0KU28gb3VyIHBsb3Qgc2hvd3MgdXMgdGhlIGdlbmVyYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSBERSBkYXRhLiBMb29raW5nIGF0IGl0LCB3ZSBjYW4gc2VlLCBub3RhYmx5LCB0aGF0IGl0IGFwcGVhcnMgdGhhdCB3ZSBoYXZlIG1hbnkgbW9yZSBnZW5lcyB1cHJlZ3VsYXRlZCB0aGFuIGRvd25yZWd1bGF0ZWQgaW4gY29tcGFyaXNvbiB0byBvdXIgY29udHJvbC4gV2UgYWxzbyBzZWUsIGFzIGV4cGVjdGVkLCBtb3JlIGxvdy1wcm9iYWJpbGl0eSB2YXJpYXRpb24gb2NjdXJyaW5nIHdpdGggbG93ZXIgbm9ybWFsaXplZCBtZWFuIGNvdW50cy4gQ29udmVyc2VseSwgYXMgb3VyIG92ZXJhbGwgbWVhbiBjb3VudHMgaW5jcmVhc2UsIHdlIGFsc28gc2VlIGxlc3MgdmFyaWF0aW9uIGluIGdlbmVyYWwgYnV0IHdlIGRvIHNlZSBhIGdlbmUgdGhhdCBkb2VzIHBhc3MgdGhyZXNob2xkIERFIHdpdGggYSBsb3cgZW5vdWdoIHAtdmFsdWUhIFRvIGludmVzdGlnYXRlIGZ1cnRoZXIgeW91IGNvdWxkIGZpbHRlciB0aGUgZGF0YSBvciBsb29rIGFnYWluIHdpdGggYSAqKnZvbGNhbm8gcGxvdCoqIQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4yLjAgVm9sY2FubyBwbG90cyBleGFtaW5lIGZvbGQtY2hhbmdlIHZzIHAtdmFsdWUNCg0KV2hpbGUgdGhlIE1BIHBsb3RzIGV4cGxvcmVkIGRhdGEgYmFzZWQgb24gaXRzIGZvbGQtY2hhbmdlIHZhbHVlcyBpbiByZWxhdGlvbiB0byBtZWFuIGV4cHJlc3Npb24sIHRoZSB2b2xjYW5vIHBsb3QgbG9va3MgcHVyZWx5IGF0IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJhc2VkIG9uIGl0J3MgKipwLXZhbHVlKiogKGllIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBERSBpcyByZWFsKS4gVG8gZW1waGFzaXplIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBwLXZhbHVlIHdlIHdpbGwgKiotbG9nIHRyYW5zZm9ybSoqIGl0IHNvIHNtYWxsZXIgcC12YWx1ZXMgYXJlIGhpZ2hlciB1cCBvbiB0aGUgeS1heGlzLiBUaGlzIHF1aWNrbHkgcGFydGl0aW9ucyBkYXRhIHBvaW50cyBpbiB0aGUgdmlzdWFsaXphdGlvbiB0byBkcmF3IGhpZ2gtREUvbG93IHAtdmFsdWUgY29tYmluYXRpb25zIGF3YXkgZnJvbSB0aGUgYnVsayBvZiB0aGUgcG9wdWxhdGlvbi4NCg0KVG8gcGxvdCB0aGlzIGRhdGEsIHdlJ2xsIHJldHVybiB0byBvdXIgbGFyZ2VyIGBibGFuY29fZGF0YV9sb25nLmRmYCB3aGljaCBjb250YWlucyBkYXRhIGZyb20gbXVsdGlwbGUgZXhwZXJpbWVudHMuIEZvciBub3csIGxldCdzIGZvY3VzIG9uIGp1c3QgdGhlIFNBUlMtQ29WLTIgZGF0YSB0aGF0IGhhcyBiZWVuIGdlbmVyYXRlZCBpbiB0aGUgQTU0OSBob3N0IGNlbGxzLiBPdXIgKi1sb2cqIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBwLXZhbHVlcyB3aWxsIGFsc28gYmUgcHJvYmxlbWF0aWMgd2hlbiBlbmNvdW50ZXJpbmcgMCwgc28gYmUgY2FyZWZ1bCEgTGV0J3MgY29tcGFyZSBwbG90cyB3aXRoIGFuZCB3aXRob3V0IHRoZXNlIHByb2JsZW0gdmFsdWVzLg0KDQpUbyBsYWJlbCBvdXIgcG9pbnRzIG9mIGludGVyZXN0LCB3ZSdsbCBhbHNvIHVzZSB0aGUgYGdlb21fdGV4dF9yZXBlbCgpYCBmcm9tIHRoZSBgZ2dyZXBlbGAgcGFja2FnZS4gVGhpcyB3aWxsIGhlbHAgYXJyYW5nZSBhbmQgcGxvdCB0ZXh0IG9udG8gb3VyIHNjYXR0ZXJwbG90IGluIGEgd2F5IHRoYXQgYXZvaWRzIGNvbGxpc2lvbnMgd2l0aCB0aGUgZGF0YSBhbmQgdGhlIG90aGVyIGxhYmVscyEgWW91IGNvdWxkIGFsc28gZXhwZXJpbWVudCB3aXRoIHVzaW5nIHRoZSBgZGlyZWN0bGFiZWxzYCBwYWNrYWdlIHRoYXQgd2UgcGxheWVkIGFyb3VuZCB3aXRoIGxhc3Qgd2VlayB0byBhY2NvbXBsaXNoIGEgc2ltaWxhciBmZWF0Lg0KDQpgYGB7cn0NCiMgUmVtaW5kIG91cnNlbHZlcyB3aGF0IG91ciBkYXRhIHRhYmxlIGxvb2tzIGxpa2UNCmhlYWQoYmxhbmNvX2RhdGFfbG9uZy5kZikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIFJlbW92ZSBwLXZhbHVlcyA9IDANCnZvbGNhbm8ucGxvdDEgPC0NCg0KICAjIFZvbGNhbm8gcGxvdCENCiAgYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQogIGZpbHRlcihwYXRob2dlbiA9PSAiU0FSUy1Db1YtMiIsIGhvc3Q9PSJBNTQ5IiwgcGFkaiAhPTApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PUwyRkMsIHkgPSAtbG9nMTAocGFkaikpICsNCiAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCiAgICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gcGxvdCB3aXRoIHAtdmFsdWVzID09IDAgcmVtb3ZlZCIpICsNCiAgDQogICAgIyA0LiBHZW9tcw0KICAgICMgcG9pbnRzIGFsbCBjb2xvdXJlZCByZWQNCiAgICBnZW9tX3BvaW50KGNvbG91ciA9ICJyZWQiLCBhbHBoYSA9IDAuNywgc2l6ZT00KSArDQogIA0KICAgICMgaGlnaGxpZ2h0IGxlYXZlcyBvbmx5IHRoZSBvbmVzIG1lZXRpbmcgb3VyIGNyaXRlcmlhIGFzIHJlZCwgY29sb3VyIHJlc3QgYXMgYmx1ZQ0KICAgIGdnaGlnaGxpZ2h0KGFicyhMMkZDKSA+IDIsIHBhZGogPCAwLjA1LCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImxpZ2h0Ymx1ZSIpKSArDQoNCiAgICAjIyMgMi4yLjANCiAgICAjIEFkZCBsYWJlbHMgYnV0IG9ubHkgZm9yIHRoZSB0b3AgMTAgZ2VuZXMNCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAjIEZpbHRlciB0aGUgZGF0YSB1c2VkIHRvIGxhYmVsDQogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoLi4uID09ICJTQVJTLUNvVi0yIiwgLi4uPT0iQTU0OSIsIGFicyhMMkZDKSA+IDIsIHBhZGogIT0wKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICMgQXJyYW5nZSBpbiBhc2NlbmRpbmcgb3JkZXINCiAgICAgICAgICAgICAgICAgICAgICAgIGFycmFuZ2UocGFkaikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAjIFRha2UgdGhlIGZpcnN0IDEwIHJlc3VsdHMNCiAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzbGljZSgxOjEwKSwNCiAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gLi4uKSwgc2l6ZT02KQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgbGVhdmUgaW4gcC12YWx1ZXMgPSAwDQp2b2xjYW5vLnBsb3QyIDwtDQoNCiAgIyBWb2xjYW5vIHBsb3QhDQogIGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICBmaWx0ZXIocGF0aG9nZW4gPT0gIlNBUlMtQ29WLTIiLCBob3N0PT0iQTU0OSIpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PUwyRkMsIHkgPSAtbG9nMTAocGFkaikpICsNCg0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfYncoKSsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogICAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIHBsb3Qgd2l0aCBhbGwgcC12YWx1ZXMiKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgIyBwb2ludHMgYWxsIGNvbG91cmVkIHJlZA0KICAgIGdlb21fcG9pbnQoY29sb3VyID0gInJlZCIsIGFscGhhID0gMC43LCBzaXplPTQpICsNCg0KICAgICMgaGlnaGxpZ2h0IGxlYXZlcyBvbmx5IHRoZSBvbmVzIG1lZXRpbmcgb3VyIGNyaXRlcmlhIGFzIHJlZCwgY29sb3VyIHJlc3QgYXMgYmx1ZQ0KICAgIGdnaGlnaGxpZ2h0KGFicyhMMkZDKSA+IDIsIHBhZGogPCAwLjA1LCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImxpZ2h0Ymx1ZSIpKSArDQoNCiAgICAjIEFkZCBsYWJlbHMgYnV0IG9ubHkgZm9yIHRoZSB0b3AgMTAgZ2VuZXMNCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgIyBGaWx0ZXIgdGhlIGRhdGEgdXNlZCB0byBsYWJlbA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHBhdGhvZ2VuID09ICJTQVJTLUNvVi0yIiwgaG9zdD09IkE1NDkiLCBhYnMoTDJGQykgPiAyKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAjIEFycmFuZ2UgaW4gYXNjZW5kaW5nIG9yZGVyDQogICAgICAgICAgICAgICAgICAgICAgICBhcnJhbmdlKHBhZGopICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICMgVGFrZSB0aGUgZmlyc3QgMTAgcmVzdWx0cw0KICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNsaWNlKDE6MTApLA0KICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBHZW5lTmFtZSksIHNpemU9NikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KIyBQbG90IGJvdGggb2Ygb3VyIGZpZ3VyZXMgdG9nZXRoZXINCmZpZy5zaG93PSJob2xkIjsgb3V0LndpZHRoPSI1MCUiDQp2b2xjYW5vLnBsb3QxDQp2b2xjYW5vLnBsb3QyDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMSBJbnRlcnByZXRpbmcgeW91ciB2b2xjYW5vIHBsb3RzDQoNCkxvb2tpbmcgYXQgb3VyIHZvbGNhbm8gcGxvdHMsIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgdm9sY2FubyBzaGFwZSB3ZSBhcmUgbG9va2luZyBmb3IuIFRoZSBkYXRhIGlzIHNwbGl0IGJldHdlZW4gb3VyICRccG0kIGxvZyRfezJ9JCBmb2xkIGNoYW5nZXMgd2l0aCBwb2ludHMgaGlnaGxpZ2h0ZWQgd2hlbiB3ZSBzZWUgREUgYmV5b25kIHRoaXMuIFlvdSBjYW4gY3VzdG9taXplIHlvdXIgcGFyYW1ldGVycyBidXQgeW91IGNhbiBhbHNvIHNlZSB0aGUgZW1waGFzaXMgb24gbG93ZXIgcC12YWx1ZXMgaW4gb3VyIGRhdGEgc2V0LiBUaGUgbW9zdCAic2lnbmlmaWNhbnQiIGRhdGEgaXMgZm91bmQgaW4gdGhlIHVwcGVyIGxlZnQgYW5kIHJpZ2h0IHF1YWRyYW50cyBvZiB0aGUgZ3JhcGgsIHdoaWNoIHdhc24ndCBtYWRlIGNsZWFyIGluIHRoZSBNQSBwbG90LiBZb3UgY291bGQgdXNlIHRoZSBzYW1lIHRyaWNrcyB0byBsYWJlbCB5b3VyIE1BIHBsb3QgdG9vIGJ1dCB0aGUgZGF0YSBjb3VsZCBlbmQgdXAgYW55d2hlcmUgYWxvbmcgdGhlIE1BIHBsb3QuDQoNCkluIHRoZSBlbmQsIGlmIHlvdSAqZG8qIGhhdmUgYWNjZXNzIHRvIHRoZSBvcmlnaW5hbCBjb3VudHMsIGl0IG1heSBiZSBvZiB1c2UgdG8gY29uc2lkZXIgdGhpcyBpbmZvcm1hdGlvbiB3aGVuIGZ1cnRoZXIgaW52ZXN0aWdhdGluZyB5b3VyIGNhbmRpZGF0ZSBnZW5lcyBmcm9tIERFLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqU2VjdGlvbiAyLjAuMCBDb21wcmVoZW5zaW9uIFF1ZXN0aW9uOioqIENvbXBhcmluZyBvdXIgdHdvIHZlcnNpb25zIG9mIHRoZSB2b2xjYW5vIHBsb3QsIHdlIHNlZSBkaWZmZXJlbnQgc2V0cyBvZiBnZW5lcyBoaWdobGlnaHRlZCBhcyB0aGUgInRvcCAxMCIgYmFzZWQgb24gYWRqdXN0ZWQgcC12YWx1ZXMuIFdoYXQgbWFrZXMgdGhlc2UgdHdvIHNldHMgb2YgZGF0YSBkaWZmZXJlbnQ/IFdoeSBkbyB3ZSBzZWUgZGF0YXBvaW50cyBhdCB0aGUgdG9wIGVkZ2Ugb2Ygb3VyIHBsb3RzIGluIHRoZSBzZWNvbmQgdmVyc2lvbj8NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAzLjAuMCBNZWRpdW0gcmVzb2x1dGlvbiBhbmFseXNpcyBmb2N1c2VzIG9uIGdyb3VwcyBvZiBnZW5lcw0KDQpOb3cgdGhhdCB3ZSd2ZSB0YWtlbiBhbiBvdmVyYWxsIGxvb2sgYXQgb3VyIGRhdGEsIHdlIGNhbiBiZWdpbiB0byBhc3Nlc3Mgb3VyIGRhdGEgYmFzZWQgb24gc29tZSBjYW5kaWRhdGUgZ2VuZXMgb3IgaHlwb3RoZXNpcyB0ZXN0aW5nLiBGb3IgaW5zdGFuY2UsIHlvdSBtYXkgb25seSBiZSBpbnRlcmVzdGVkIGluIGxvb2tpbmcgYXQgZ2VuZXMgd2l0aCBhbiBMMkZDIFw+IDMuIEluIG90aGVyIGNhc2VzLCB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gYSBncm91cCBvZiBnZW5lcyBwZXJ0YWluaW5nIHRvIGEgZnVuY3Rpb24gbGlrZSB0aGUgY2hlbW9raW5lcyB3aGljaCBhcmUgcGFydCBvZiB0aGUgaW5mbGFtbWF0aW9uIHJlc3BvbnNlLg0KDQpBZnRlciBzZWxlY3RpbmcgYSBzdWJzZXQgb2YgZ2VuZXMgd2UgY2FuIGJlZ2luIHRvIGNvbXBhcmUgdGhlaXIgREUgZGF0YSBtb3JlIG1lYW5pbmdmdWxseSBhY3Jvc3MgZGlmZmVyZW50IHNhbXBsZSBzZXRzLiBMZXQncyBjb250aW51ZSB3b3JraW5nIHdpdGggb3VyIGxvbmctZm9ybWF0IGRhdGFzZXQgYGJsYW5jb19kYXRhX2xvbmcuZGZgIHdoaWNoIGNvbnRhaW5zIG91ciBtdWx0aXBsZSBSTkFzZXEgZXhwZXJpbWVudHMgZnJvbSB2YXJpb3VzIGluZmVjdGlvbiBjb25kaXRpb25zLiBXZSB3aWxsIGJlZ2luIGJ5IGdlbmVyYXRpbmcgYSBsaXN0IG9mIHRoZSBoaWdoZXN0IERFIGdlbmVzIHdpdGggbG93IHAtdmFsdWVzIGluIHRoZSBTQVJTLUNvVi0yIGluZmVjdGlvbiBzY2VuYXJpby4NCg0KTm90ZSBoZXJlIHdlJ2xsIHVzZSB0aGUgYHB1bGwoKWAgZnVuY3Rpb24gd2hpY2ggaXMgYSBuaWNlci1sb29raW5nIHdheSB0byByZXRyaWV2ZSBhICpzaW5nbGUqIGNvbHVtbiBhcyBhIHZlY3RvciBmcm9tIG91ciBgdGliYmxlYCBvYmplY3QuIFRoaXMgaXMgZXF1aXZhbGVudCB0byB1c2luZyBhIGAuJGNvbE5hbWVgIGZvcm1hdC4NCg0KYGBge3J9DQojIFNlbGVjdCB0aGUgZ2VuZXMgd2Ugd2FudCB0byB2aXN1YWxpemUNCmhlYXRtYXBfZ2VuZS5saXN0IDwtIA0KICBibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgDQogICMgZmlsdGVyIGZvciBkYXRhIGZyb20gYSBzcGVjaWZpYyBleHBlcmltZW50LCBhbmQgdGhlbiBieSBoaWdoIHBvc2l0aXZlIEwyRkMgdmFsdWVzIHdpdGggbG93IHAtdmFsdWVzDQogIGZpbHRlcihwYXRob2dlbiA9PSAiU0FSUy1Db1YtMiIsIGhvc3QgPT0gIkE1NDkiLCBMMkZDID4gLi4uLCBwYWRqIDwgLi4uKSAlPiUgDQogIC4uLg0KICANCiAgIyBUYWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0aW5nIGxpc3QNCiAgaGVhdG1hcF9nZW5lLmxpc3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4xLjAgVXNlIGhlYXRtYXBzIHRvIHZpc3VhbGl6ZSBkYXRhIGFjcm9zcyBhIGNvbnRpbnVvdXMgcmFuZ2Ugb2YgdmFsdWVzDQoNCk5vdyB0aGF0IHdlIGhhdmUgZ2VuZXJhdGVkIGEgc2VsZWN0IGdyb3VwIG9mIGdlbmVzIHRvIGV4YW1pbmUsIG91ciBERSBkYXRhIGlzIHdlbGwtc3VpdGVkIHRvIHZpc3VhbGl6aW5nIGluIGhlYXRtYXAgb3IgaGVhdCBwbG90LiBJbiBvdXIgaGVhdHBsb3Qgd2UgY2FuIGdlbmVyYXRlIHZhbHVlcyBhbG9uZyB0d28gY2F0ZWdvcmljYWwgYXhlcy4gT24gdGhlIHktYXhpcyB3ZSBjYW4gcGxvdCBERSB2YWx1ZXMgZnJvbSBvdXIgaW5kaXZpZHVhbCBnZW5lcywgd2hpbGUgb24gdGhlIHgtYXhpcyB3ZSB3aWxsIHNob3cgdmFsdWVzIGFjcm9zcyBtdWx0aXBsZSBleHBlcmltZW50cy4gVG8gdmlzdWFsaXplIHRoZSBkYXRhIGl0c2VsZiwgd2Ugd2lsbCB1c2UgdGhlIGBnZW9tX3RpbGUoKWAgY29tbWFuZC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KIyBoZWF0bWFwIG9mIG91ciBkYXRhIGJhc2VkIG9uIGEgbGlzdCBvZiB0b3AgREUgZ2VuZXMgZnJvbSB0aGUgU0FSUy1Db1YtMiBpbmZlY3Rpb24gb2YgQTU0OSBjZWxscw0KDQpibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgZmlsdGVyKEdlbmVOYW1lICVpbiUgaGVhdG1hcF9nZW5lLmxpc3QsDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICAjIG11dGF0ZShHZW5lTmFtZSA9IGZhY3RvcihHZW5lTmFtZSwgbGV2ZWxzID0gaGVhdG1hcF9nZW5lLmxpc3QpKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjMi4gQWVzdGhldGljcw0KICAgICMjIyAzLjEuMCBzZXQgYWVzdGhldGljcw0KICAgIGFlcyh4PS4uLiwgeSA9IC4uLiwgZmlsbD0uLi4pICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkNCiAgICAgICAgICkrDQogICAgbGFicyh0aXRsZSA9ICJIZWF0bWFwIG9mIEwyRkMgdmFsdWVzIGluIEE1NDkgY2VsbHMgaW5mZWN0ZWQgYnkgZGlmZmVyZW50IHBhdGhvZ2VucyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgICMgS2VlcCB0aGlzIHRvIHJldmVyc2UgdGhlIHkgYXhpcyBvciBwdXQgaXQgaW50byB0aGUgYXNzaWdubWVudD8NCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldikgKyANCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb249InBsYXNtYSIpICsNCg0KICAgICMgR2VvbXMNCiAgICAjIyMgMy4xLjAgVXNlIGEgdGlsZSBhbmQgc2V0IHRoZSB3aWR0aCBhIGxpdHRsZSBzbWFsbGVyIHRvIGdpdmUgYSBnYXAgYmV0d2VlbiBncm91cHMNCiAgICBnZW9tX3RpbGUoLi4uKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTR9DQoNCiMgaGVhdG1hcCBvZiBvdXIgZGF0YSBiYXNlZCBvbiBhIGxpc3Qgb2YgdG9wIERFIGdlbmVzIGZyb20gdGhlIFNBUlMtQ29WLTIgaW5mZWN0aW9uIG9mIEE1NDkgY2VsbHMNCiMgU3BsaXQgeC1heGlzIGJ5IGV4cGVyaW1lbnQgbmFtZQ0KDQpibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgZmlsdGVyKEdlbmVOYW1lICVpbiUgaGVhdG1hcF9nZW5lLmxpc3QsDQogICAgICAgICAgIC4uLg0KICAgICAgICApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9ZXhwZXJpbWVudCwgeSA9IEdlbmVOYW1lLCBmaWxsPUwyRkMpICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkNCiAgICAgICAgICkgKw0KICAgIGxhYnModGl0bGUgPSAiSGVhdG1hcCBvZiBMMkZDIHZhbHVlcyBpbiBTQVJTLUNvVjIgaW5mZWN0aW9ucyBieSBleHBlcmltZW50IikgKw0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgIyBLZWVwIHRoaXMgdG8gcmV2ZXJzZSB0aGUgeSBheGlzIG9yIHB1dCBpdCBpbnRvIHRoZSBhc3NpZ25tZW50Pw0KICAgIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KSArIA0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0icGxhc21hIikgKw0KDQogICAgIyBHZW9tcw0KICAgICMgVXNlIGEgdGlsZSBhbmQgc2V0IHRoZSB3aWR0aCBhIGxpdHRsZSBzbWFsbGVyIHRvIGdpdmUgYSBnYXAgYmV0d2VlbiBncm91cHMNCiAgICBnZW9tX3RpbGUod2lkdGggPSAwLjk1KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xLjEgSW50ZXJwcmV0aW5nIGEgaGVhdG1hcCBvZiB1cHBlciB2YWx1ZXMNCg0KRnJvbSBvdXIgbGlzdCBvZiB0aGUgdG9wIDE4IERFIGhpdHMgaW4gb3VyIFNBUlMtQ29WLTIgc2V0LCBpdCBsb29rcyBsaWtlIHdlIHNlZSBzb21lIHNpbWlsYXIgaGl0cyBhY3Jvc3Mgc29tZSBvZiB0aGUgZ2VuZXMgd2hlbiBpbmZlY3RpbmcgQTU0OSBjZWxscyB3aXRoIG90aGVyIHZpcnVzZXMgbGlrZSBIUElWMyBhbmQgUlNWLg0KDQpXZSBzZWUgc3Ryb25nIEwyRkMgdmFsdWVzIGZvciBJTDYsIElMMUEgYW5kIExBTUMyIGZvciBpbnN0YW5jZS4gSUw2IChpbnRlcmxldWtpbiA2KSBoYXMgYmVlbiByZXBvcnRlZCB0byBwbGF5IGEgcm9sZSBpbiBib3RoIG1vdW50aW5nIGFuIGVmZmVjdGl2ZSBpbW11bmUgcmVzcG9uc2UgdG8gY2VydGFpbiB2aXJhbCBpbmZlY3Rpb25zIGJ1dCBpdHMgaW50ZXJhY3Rpb25zIHdpdGggb3RoZXIgZmFjdG9ycyBtYXkgYWxzbyBoYXZlIGEgcG90ZW50aWFsIHJvbGUgaW4gdGhlIGV4YWNlcmJhdGlvbiBvZiB2aXJhbCBwaGVub3R5cGVzLiBJbiBsb29raW5nIGFjcm9zcyBkaWZmZXJlbnQgaG9zdCBjZWxsIGluZmVjdGlvbnMgYnkgU0FSUy1Db1YtMiB0aGVyZSBpcyBhZ2FpbiBjb25zaXN0ZW50IHVwcmVndWxhdGlvbiBvZiBJTDFBIGFuZCBJTDYuDQoNClVzaW5nIGEgc2ltaWxhciBtZXRob2QsIHdlIGNhbiBpbnN0ZWFkIGZvY3VzIG9uIGdlbmVzIGZyb20gYSBzcGVjaWZpYyBmYW1pbHkgb3IgcGF0aHdheSBhcyBsb25nIGFzIHdlIGhhdmUgYSBsaXN0LiBMZXQncyB0cnkgdGhlIGZhbWlseSBvZiBjaGVtb2tpbmVzIHdoaWNoIHBsYXkgYSByb2xlIGluIGluZHVjaW5nIGNlbGwgbW92ZW1lbnQgZHVyaW5nIHRoZSBpbW11bmUgcmVwb25zZS4NCg0KYGBge3J9DQojIEJ1aWxkIGEgbGlzdCBvZiB0aGUgcG90ZW50aWFsIGNoZW1va2luZXMgYnkgbG9va2luZyBmb3IgdGhvc2UgbWF0Y2hpbmcgdGhlICJDQ0wiIHBhdHRlcm4NCmhlYXRtYXBfY2NsLmxpc3QgPC0gDQogIGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICANCiAgIyBGaWx0ZXIgc3BlY2lmaWNhbGx5IGZvciBhbnkgZ2VuZXMgdGhhdCBoYXZlIENDTCBpbiB0aGVpciBuYW1lDQogIGZpbHRlciguLi4pICU+JSANCiAgcHVsbChHZW5lTmFtZSkgJT4lIA0KICB1bmlxdWUoKQ0KDQpzdHIoaGVhdG1hcF9jY2wubGlzdCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KTm93IHdlIGNhbiB1c2UgdGhpcyBsaXN0IGluIG91ciBgZ2dwbG90YCB2aXN1YWxpemF0aW9uLiBOb3RlIHRoYXQgd2UgKmNvdWxkKiBoYXZlIGdlbmVyYXRlZCBhIG11Y2ggbG9uZ2VyIHBpcGUgc2VyaWVzIHdpdGhvdXQgc2F2aW5nIHRoZSBvYmplY3QgYGhlYXRtYXBfY2NsLmxpc3RgIGJ1dCB0aGVuIHdlIHdvdWxkbid0IGhhdmUgYSBjaGFuY2UgdG8gZXhhbWluZSBpdCBiZWZvcmUgcGxvdHRpbmcgdGhhdCBkYXRhIHRvIGxlYXJuIHRoYXQgaXQgaGFzIDI5IHVuaXF1ZSB2YWx1ZXMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgaGVhdG1hcCBvZiBvdXIgZGF0YSBiYXNlZCBvbiBhIGxpc3Qgb2YgY2hlbW9raW5lIGdlbmVzDQoNCmJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KDQogICMgRmlsdGVyIGZvciBnZW5lcyBpbiBvdXIgbGlzdCwgYW5kIGV4cGVyaW1lbnRzIHdoZXJlIHRoZSBob3N0IGNlbGwgd2FzICJBNTQ5Ig0KICBmaWx0ZXIoR2VuZU5hbWUgJWluJSAuLi4sDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIzIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1leHBlcmltZW50LCB5ID0gR2VuZU5hbWUsIGZpbGw9TDJGQykgKw0KDQogICAgIyBUaGVtZQ0KICAgIHRoZW1lX2J3KCkrDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3QgPSAxKQ0KICAgICAgICAgKSsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0icGxhc21hIikrDQoNCiAgICAjIEdlb21zDQogICAgIyBVc2UgYSB0aWxlIGFuZCBzZXQgdGhlIHdpZHRoIGEgbGl0dGxlIHNtYWxsZXIgdG8gZ2l2ZSBhIGdhcCBiZXR3ZWVuIGdyb3Vwcw0KICAgIGdlb21fdGlsZSh3aWR0aCA9IDAuOTUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjEuMiBIZWF0bWFwIGludGVycHJldGF0aW9uDQoNCkZyb20gb3VyIGhlYXRtYXAsIHdlIGNhbiBvYnNlcnZlIHRoYXQgbG9va2luZyBvbmx5IGF0IHRoZSBjaGVtb2tpbmVzLCB0aGVyZSBpcyBjb25zaXN0ZW50IHVwcmVndWxhdGlvbiBvZiBDQ0w1IGFuZCBDQ0wyIHdoZW4gaW5mZWN0aW5nIG91ciBBNDU5IGNlbGxzIHdpdGggYW55IG9mIHRoZXNlIHZpcnVzZXMuIEluIGNvbXBhcmlzb24sIGhvd2V2ZXIsIEhQSVYzIGFuZCBSU1YgaW5mZWN0aW9uIHNob3cgYSBtdWNoIGhpZ2hlciBERSBhY3Jvc3MgdGhlIENDTCBmYW1pbHkgaW4gY29tcGFyaXNvbiB0byBTQVJTLUNvVi0yIHdoaWNoIGFwcGVhcnMgdG8gaWxsaWNpdCBsZXNzIGluZmxhbW1hdG9yeSByZXNwb25zZSBpbiB0aGUgdGltZS1mcmFtZSBzYW1wbGVkLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKkJ1dCB3YWl0IHRoZXJlJ3MgbW9yZSEqKiBXaGlsZSB3ZSBhcmUgZGVhbGluZyB3aXRoIGp1c3QgYSBzdWJzZXQgb2Ygb3VyIFJOQS1TZXEgZGF0YSwgbXVjaCBtb3JlIGlzIGF2YWlsYWJsZSB0byB2aXN1YWxpemUgb24gYSBncmVhdGVyIHNjYWxlLiBOZXh0IHdlZWsgd2UnbGwgZGlnIGRlZXBlciBpbnRvIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSBhbmQgaG93IGhlYXRtYXBzIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBhbmQgb3JnYW5pemUgdGhpcyBraW5kIG9mIGluZm9ybWF0aW9uIHRvIGhlbHAgaWRlbnRpZnkgYW5kIGNvbnZleSBwYXR0ZXJucy4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4yLjAgTG9va2luZyBhdCBnZW5lcyB3aXRoIGRvdCBwbG90cw0KDQpOb3RlIHRoYXQgaW4gdGhlIGFib3ZlIGhlYXRtYXAgd2Ugbm8gbG9uZ2VyIGhhdmUgYW55IGluZm9ybWF0aW9uIG9uIHRoZSBhZGp1c3RlZCBwLXZhbHVlcyBmcm9tIHRoZXNlIEwyRkMgc2NvcmVzLiBTb21ldGhpbmcgdG8ga2VlcCBpbiBtaW5kIHdoZW4gbWFraW5nIHRoZXNlIGhlYXRtYXBzLiBXaGF0IHdvdWxkIGhhcHBlbiBpZiB3ZSBmaWx0ZXJlZCBvbiBwLXZhbHVlcyBhcyB3ZWxsPyBJcyB0aGVyZSBhIHdheSB3ZSBjb3VsZCB0cmFjayB0aGF0IGluZm9ybWF0aW9uPw0KDQpJZiB3ZSByZXBsYWNlIG91ciBgZ2VvbV90aWxlKClgIHdpdGggYSBgZ2VvbV9wb2ludCgpYCB3ZSBhcmUgYWJsZSB0byBhZGQgYW4gYWRkaXRpb25hbCBkaW1lbnNpb24gdG8gb3VyIGRhdGEgYnkgdXRpbGl6aW5nIGBzaXplYCBtYXBwaW5nLg0KDQpXZSdsbCByZXBlYXQgb3VyIHZpc3VhbGl6YXRpb24gZnJvbSBhYm92ZSBidXQgdXNlIGNvbG91ciB0byByZXByZXNlbnQgdGhlIGBMMkZDYCB2YWx1ZXMgKmFuZCogYSBzaXplIHRvIHJlcHJlc2VudCB0aGUgYHBhZGpgIHZhbHVlcyBpbiBvdXIgZGF0YXNldC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KIyBoZWF0bWFwIG9mIG91ciBkYXRhIGJhc2VkIG9uIGEgbGlzdCBvZiBjaGVtb2tpbmUgZ2VuZXMNCg0KYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQoNCiAgIyBGaWx0ZXIgZm9yIGdlbmVzIGluIG91ciBsaXN0LCBhbmQgZXhwZXJpbWVudHMgd2hlcmUgdGhlIGhvc3QgY2VsbCB3YXMgIkE1NDkiDQogIGZpbHRlcihHZW5lTmFtZSAlaW4lIGhlYXRtYXBfY2NsLmxpc3QsDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIzIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1leHBlcmltZW50LCB5ID0gR2VuZU5hbWUpICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkNCiAgICAgICAgICkrDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKG9wdGlvbj0idmlyaWRpcyIsICkgKw0KICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgMTApLCBicmVha3MgPSBjKDAsIDEuMywgMTAsIDEwMCkpICsNCg0KICAgICMgR2VvbXMNCiAgICAjIyMgMy4yLjAgVXNlIGNvbG91ciBhbmQgc2l6ZSBpbiBvdXIgZG90cGxvdCB0byByZXByZXNlbnQgTDJGQyBhbmQgcGFkag0KICAgIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IC4uLiwgc2l6ZSA9IC4uLikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk5vdyB3ZSBjYW4gbW9yZSBjbGVhcmx5IHNlZSB3aGljaCB2YWx1ZXMgYXJlIHdvcnRoIG5vdGluZyBvciBpbnZlc3RpZ2F0aW5nLiBXZSBjYW4gc2VlIHRoYXQgQ0NMMiBoYXMgYSBtb3JlIGNvbnNpc3RlbnQgYEwyRkNgIGFuZCBgcGFkamAgY29tYmluYXRpb24gYWx0aG91Z2ggdGhpcyB3b3VsZCByZXF1aXJlIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiBzaW5jZSBvdXIgc2l6ZSByYW5nZSBpcyBzdGlsbCBxdWl0ZSBsaW1pdGVkLiBXZSBjYW4gZnVydGhlciB6b29tIGluIG9uIG91ciBkYXRhIGJ5IHNwZWNpZmljYWxseSB2aXN1YWxpemluZyBvdXIgZ2VuZXMgb2YgaW50ZXJlc3QhDQoNCiMjIyAzLjIuMSBMb29raW5nIGF0IHNpbmdsZSBnZW5lcyB3aXRoIGRvdCBwbG90cw0KDQpXZSBjYW4gZnVydGhlciBuYXJyb3cgb3VyIHNlYXJjaCBpbnN0ZWFkIG9mIGxvb2tpbmcgYXQgKndob2xlIGdlbmUgZ3JvdXBzKiwgYW5kIHBpY2sgYSBzbWFsbCBzZXQgb2Ygc3BlY2lmaWMgZ2VuZXMuIFdlJ2xsIHVzZSBhIGRvdHBsb3QgdG8gdmlzdWFsaXplIHRoZSBkYXRhIHdoZXJlIHRoZSB5LWF4aXMgdXNlcyB0aGUgTDJGQyB2YWx1ZSB0byBwbGFjZSBwb2ludHMgYW5kIHdlJ2xsIHVzZSBhIGNhdGVnb3JpY2FsIHgtYXhpcyBvZiBnZW5lIGxpc3RzLiBXZSdsbCBmaWx0ZXIgb3VyIGRhdGEgdG8gb25seSBpbmNsdWRlIGluZmVjdGlvbnMgaW4gdGhlIEE1NDkgaG9zdCwgYW5kIGFsc28gZ3JvdXAgb3VyIGRhdGEgaW4gYSBmZXcgd2F5czoNCg0KMS4gIFdlJ2xsIHNjYWxlIG91ciBwb2ludCBzaXplcyBiYXNlZCBvbiB0aGUgYWRqdXN0ZWQgcC12YWx1ZSB0byBnZXQgYSBiZXR0ZXIgc2Vuc2Ugb2YgaG93IHByb2JhYmxlIHRoZXNlIGNoYW5nZXMgYXJlLg0KMi4gIFdlJ2xsIHVzZSBjb2xvdXIgdG8gZGVmaW5lIHRoZSBwYXRob2dlbiB1c2VkIGluIGVhY2ggZXhwZXJpbWVudC4NCjMuICBXZSdsbCBhbHNvIGZhY2V0IG91ciBkYXRhIGJhc2VkIG9uIGhvc3QuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTR9DQoNCnNpbmdsZV9nZW5lcyA9IGMoIkNDTDUiLCAiQ0NMMiIsICJDQ0wyMCIsICJDQ0wxNyIsICJJTDYiLCAiSUwxQSIpDQoNCmJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICBmaWx0ZXIoR2VuZU5hbWUgJWluJSBzaW5nbGVfZ2VuZXMsDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgIyMjIDMuMi4wIHNldCB0aGUgYWVzdGhldGljcyBmb3Igb3VyIGRvdCBwbG90IGNvbG91cmluZyBieSBwYXRob2dlbiwgc2hhcGluZyBieSBob3N0DQogICAgYWVzKHg9R2VuZU5hbWUsIHkgPSBMMkZDLCBjb2xvdXI9Li4uLCBzaGFwZT0uLi4pICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSkpKSArDQogICAgIyBTY2FsZSB0aGUgc2l6ZSByYW5nZQ0KICAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDgsIDQpKSArDQogICAgc2NhbGVfc2hhcGUoZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSkpKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgIyMjIDMuMi4wIGFkanVzdCB0aGUgZG90IHNpemUgYmFzZWQgb24gdGhlIGFkanVzdGVkIHAtdmFsdWUNCiAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gLi4uKSwgYWxwaGEgPSAwLjgsIHN0cm9rZSA9IDIpICsNCg0KICAgICMgNi4gRmFjZXRzDQogICAgZmFjZXRfd3JhcCh+IGhvc3QsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMi4xIERvdCBwbG90IGludGVycHJldGF0aW9uDQoNCkZvY3VzaW5nIG9uIHNtYWxsIHNldHMgb2YgZ2VuZXMgd2UgY2FuIHNlZSBzb21lIHRyZW5kcyBhY3Jvc3Mgb3VyIGdyb3Vwcy4gRm9yIGluc3RhbmNlLCAzIG9mIG91ciBwYXRob2dlbnMgcHJvZHVjZSBhIHN0cm9uZyBJTDYgcmVzcG9uc2UgYXMgd2UndmUgc2VlbiBpbiBvdXIgaGVhdG1hcHMuIFdlIGFsc28gc2VlIGEgXD4yIEwyRkMgaW4gQ0NMMiBhcyBhIHJlc3BvbnNlIHRvIGluZmVjdGlvbiBieSBTQVJTLUNvVi0yLiBBY3R1YWxseSBvdXIgd2Vha2VzdCBJTDYgcmVzcG9uc2UgdG8gU0FSUy1Db1YtMiB3YXMgc2VlbiBpbiB0aGUgQUNFLTItZXhwcmVzc2luZyBBNDU5IGhvc3RzIHNvIG91ciBpbml0aWFsIGhlYXRtYXAgaW4gc2VjdGlvbiAzLjEuMCBkaWRuJ3QgZ2l2ZSB1cyBhIGZ1bGwgcGljdHVyZSBvZiB3aGF0IG1pZ2h0IGJlIGhhcHBlbmluZy4NCg0KSXQgd291bGQgYmUgYmVzdCB0byBmdXJ0aGVyIGludmVzdGlnYXRlIHRoZXNlIG9uIGEgbW9yZSBzcGVjaWZpYyBzdWJzZXQgb2YgaG9zdHMgYW5kL29yIHBhdGhvZ2Vucy4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1kYW5nZXJ9DQoqKkNvbXByZWhlbnNpb24gUXVlc3Rpb24gMy4wLjAqKiBXaGlsZSB3ZSBzZWUgc29tZSBpbmNvbnNpc3RlbmN5IGluIG91ciBJTDYvU0FSUy1Db1YtMiByZXNwb25zZSBpbiB0aGUgYWJvdmUgZG90cGxvdCwgYXJlIHdlIHBvc3NpYmx5IG1pc3Npbmcgc29tZSBjb250ZXh0PyBJZiB5b3Ugd2VyZSB0byByZXR1cm4gdG8gdGhlIHNvdXJjZSBvZiB0aGlzIGRhdGEgcHJpb3IgdG8gb3VyIHdyYW5nbGluZyAoKipzZWUgc2VjdGlvbiAxLjUuMioqKSwgeW91IG1pZ2h0IG5vdGUgdGhhdCB0aGVyZSBhcmUgMyBraW5kcyBvZiBBNTQ5LUFDRTIgZXhwZXJpbWVudGFsIGdyb3Vwcy4gV2hhdCBhcmUgc29tZSB3YXlzIHlvdSBjb3VsZCBhY2NvdW50IGZvciB0aGlzIGluZm9ybWF0aW9uIHdoZW4gd3JhbmdsaW5nPw0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIFJOQXNlcSBhbmFseXNpcyBmb3Igc2lnbmlmaWNhbnQgZnVuY3Rpb25hbCB0ZXJtcw0KDQpVcCB1bnRpbCB0aGlzIHBvaW50LCB3ZSd2ZSBiZWVuIGZlZWxpbmcgb3VyIHdheSB0aHJvdWdoIHRoZSBkYXRhLCByYXRoZXIuLi4gKnVuZGlyZWN0ZWQqLiBPZnRlbiB3aXRoaW4gbWFudXNjcmlwdHMgeW91IHdpbGwgZmluZCBkZWVwZXIgYW5hbHlzZXMgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYnkgZXhhbWluaW5nIHRoZSBjb2xsZWN0aW9uIG9mIGZ1bmN0aW9uYWwgdGVybXMgdG8gZGV0ZXJtaW5lIHRyZW5kcyBvciBwYXRod2F5cyBvZiBpbnRlcmVzdC4gVGhlIEdlbmUgT250b2xvZ3kgKEdPKSByZXNvdXJjZSByZXByZXNlbnRzIGEgY29kaWZpY2F0aW9uIG9mIGdlbmUgZnVuY3Rpb24gdGhyb3VnaCB0aHJlZSBkb21haW5zOiBjZWxsdWxhciBjb21wb25lbnQgKENDKSwgbW9sZWN1bGFyIGZ1bmN0aW9uIChNRiksIGFuZCBiaW9sb2dpY2FsIHByb2Nlc3MgKEJQKS4NCg0KTW9zdCwgaWYgbm90IGFsbCwgb2YgdGhlIGdlbmVzIGluIG91ciBkYXRhc2V0IGNhbiBiZSBkZXNjcmliZWQgaW4gc29tZSB3YXkgYnkgYSBHTyB0ZXJtLiBPbmNlIHdlIGNvbGxlY3QgdGhlc2UgdGVybXMsIHdlIGNhbiBiZWdpbiB0byBsb29rIGZvciBtZWFuaW5nZnVsIGdyb3VwcyB0aGF0IGNvdWxkIGJlIGNvbnNpZGVyZWQgb3Zlci1yZXByZXNlbnRlZCAob3IgdW5kZXItcmVwcmVzZW50ZWQpIGluIG91ciBkYXRhc2V0LiBPbmNlIHdlIGhhdmUgY29sbGVjdGVkIHRoaXMgaW5mb3JtYXRpb24gd2UgY2FuIHZpc3VhbGl6ZSBpdCBzaW1pbGFyIHRvIG91ciBpbmRpdmlkdWFsIGdlbmUgZG90cGxvdHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjEuMCBVc2UgdGhlIGBnb3NlcWAgcGFja2FnZSB0byBwdWxsIGRvd24gR08gdGVybXMgZm9yIHlvdXIgZ2VuZXMNCg0KVXNpbmcgdGhlIGBnb3NlcWAgcGFja2FnZSB3ZSBjYW4gcGVyZm9ybSBhIGZ1bGwgR08gdGVybSBhbmFseXNpcyBvbiBvdXIgREUgZGF0YSB3aXRoIGFuIGFuYWx5c2lzIGZvciBlbnJpY2hlZCB0ZXJtcyB3aXRoaW4gb3VyIHNldCBvZiBvdmVyLSBvciB1bmRlci1leHByZXNzZWQgZ2VuZXMuIEhvd2V2ZXIsIGluIG9yZGVyIHRvIGJlZ2luIHRoZSBwcm9jZXNzIG9mIHB1bGxpbmcgZG93biBHTyB0ZXJtcywgd2l0aCB0aGUgYGdvc2VxYCBwYWNrYWdlLCB3ZSBhbHNvIG5lZWQgdG8gZ2VuZXJhdGUgYSBuYW1lZCBpbnRlZ2VyIHZlY3RvciB0byBzcGxpdCBvdXIgZ2VuZSBzZXRzIGludG8gdGhlIHR3byByZWxldmFudCBjYXRlZ29yaWVzLg0KDQpXZSdsbCBzdG9yZSBvdXIgY2F0ZWdvcml6ZWQgc2V0IG9mIGdlbmVzIGluIGBnZW5lc19TQVJTX0NvVl8yYCB3aGVyZSBhIDEgcmVwcmVzZW50cyBvdmVyL3VuZGVyZXhwcmVzc2VkIGdlbmVzIGluIG91ciBzZXQsIGFuZCAwIHJlcHJlc2VudHMgYWxsIHJlbWFpbmluZyBnZW5lcy4NCg0KYGBge3J9DQojIFNldCB1cCB0aGUgZ2VuZSBpbmZvcm1hdGlvbiB3ZSBuZWVkIGZvciB0aGUgR08tdGVybSBhbmFseXNpcw0KIyBJdCBuZWVkcyB0byBrbm93IHdoaWNoIGdlbmVzIG1lZXQgb3VyIEZDIGNyaXRlcmlhIGFuZCB3aGljaCBkbyBub3QNCiNucm93KGJsYW5jb19kYXRhLmRmKQ0KDQojIENhc3Rpbmcgb3VyIGxvZ2ljYWwgYXMgYW4gaW50ZWdlciB3aWxsIGNyZWF0ZSBhIDAvMSBtYXRyaXgNCmdlbmVzX1NBUlNfQ29WXzIgPC0gYXMuaW50ZWdlcihibGFuY29fZGF0YS5kZiRgcGFkal9TQVJTLUNvVi0yKEE1NDkpYCA8IDAuMDUgJiAjIGxvdyBwLXZhbHVlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWJzKGJsYW5jb19kYXRhLmRmJGBTQVJTLUNvVi0yKEE1NDkpX0wyRkNgKSA+IDEuNSkgIyBhYnNvbHV0ZSBMMkZDID4gMS41DQoNCiMgV2UnbGwgbmFtZSBlYWNoIGVsZW1lbnQgaW4gb3VyIHZlY3RvciBieSB0aGUgZ2VuZSBuYW1lcw0KbmFtZXMoLi4uKSA8LSBibGFuY29fZGF0YS5kZiRHZW5lTmFtZQ0KDQojIFdoYXQgaXMgb3VyIG9iamVjdD8NCnN0cihnZW5lc19TQVJTX0NvVl8yKQ0KDQojIFN1bW1hcml6ZSBvdXIgdmVjdG9yDQp0YWJsZShnZW5lc19TQVJTX0NvVl8yKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpTbyBhIHF1aWNrIGJpdCBvZiBmaWx0ZXJpbmcgYW5kIHdlIGNhbiBzZWUgZnJvbSBvdXIgdGFibGUgc3VtbWFyeSwgdGhhdCB3ZSBoYXZlIGNsb3NlIHRvIDUwMCBnZW5lcyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhYm92ZSBvciBiZWxvdyAxLjUgTDJGQy4gTm93IHdlIGhhdmUgdG8gZmlndXJlIG91dCBob3cgdG8gbWFwIHRoZXNlIHRvIEdPIHRlcm1zIQ0KDQojIyMgNC4xLjEgQ2hvb3NlIHRoZSBjb3JyZWN0IGRhdGFiYXNlIHRvIG1hcCB5b3VyIGdlbmUgc3ltYm9scyB0byBHTyB0ZXJtcw0KDQpUaGUgYGdvc2VxYCBwYWNrYWdlIGlzIGNvbXBhdGlibGUgd2l0aCBhIG51bWJlciBvZiBvcmdhbmlzbXMgYW5kIGdlbmUgbmFtZXMgZm9yIG1hcHBpbmcgeW91ciBkYXRhIHRvIHRoZSBHTyBkYXRhYmFzZS4gVG8gdmlldyAqd2hpY2gqIG9yZ2FuaXNtcyBhcmUgc3VwcG9ydGVkLCB3ZSBjYW4gdXNlIGBzdXBwb3J0ZWRPcmdhbmlzbXMoKWAgdG8gdmlldy4gU2luY2UgdGhlIHJhdyBSTkEtU2VxIHJlYWRzIHdlcmUgYWxpZ25lZCB0byB0aGUgaHVtYW4gZ2Vub21lIHVzaW5nIHRoZSBoZzE5IGFzc2VtYmx5IChzZWUgQmxhbmNvLU1lbG8gZXQgYWwuLCAyMDIwKSwgd2UnbGwgYmUgd29ya2luZyB3aXRoIGBoZzE5YCBhcyB3ZWxsLiBXZSdsbCB1c2UgdGhlIGFubm90YXRpb24gaW5mb3JtYXRpb24gcHJvdmlkZWQgZm9yIHRoZSBuZXh0IHBhcnQgb2Ygb3VyIGFuYWx5c2lzLg0KDQpGaXJzdCwgaG93ZXZlciwgbGV0J3MgY2hlY2sgdGhhdCBoZzE5IGlzIGluZGVlZCBhIHN1cHBvcnRlZCBnZW5vbWUgYW5ub3RhdGlvbi4NCg0KYGBge3J9DQojIFB1bGwgZG93biB0aGUgc3VwcG9ydGVkIG9yZ2FuaXNtcyBhbmQgbG9vayBmb3IgdGhlIGh1bWFuICJoZzE5IiBnZW5vbWUNCnN1cHBvcnRlZE9yZ2FuaXNtcygpICU+JSANCmZpbHRlciguLi4pDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjEuMiBGaXR0aW5nIGEgcHJvYmFiaWxpdHkgd2VpZ2h0aW5nIGZ1bmN0aW9uIChQV0YpIHRvIG91ciBkYXRhc2V0DQoNCkxvb2tpbmcgYXQgb3VyIHRhYmxlIGFib3ZlIHdlIGNhbiBpbW1lZGlhdGVseSBzZWUgdGhhdCB0aGVyZSBhcmUgMyBwb3RlbnRpYWwgYW5ub3RhdGlvbnMgc2V0cyB0byBkcmF3IGZyb20uIEVhY2ggdXNlcyBhIGRpZmZlcmVudCBraW5kIG9mIElEIGRlc2NyaXB0aW9uOiBFbnRyZXogR2VuZSBJRCwgRW5zZW1ibGUgZ2VuZSBJRCwgYW5kIEdlbmUgU3ltYm9sLiBXaGljaCBvbmUgZG8gd2UgdXNlPyBUaGlzIGRlcGVuZHMgb24gaG93IG91ciBkYXRhIGlzIGZvcm1hdHRlZCBidXQgbGV0J3MgdGFrZSBhIHF1aWNrIGxvb2sgYXQgdGhpcyBzY3JlZW5zaG90IG9mIENDTDggZW50cnkgZnJvbSBOQ0JJOg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0dlbmVJbmZvcm1hdGlvbi5wbmc/cmF3PXRydWUiIHdpZHRoPSI4MDAiLz4NCg0KSXQncyBpbXBvcnRhbnQgdG8ga25vdyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHlvdXIgZ2VuZSBzeW1ib2xzIGFuZCB2YXJpb3VzIGlkZW50aWZpZXJzIQ0KOjo6DQoNCktub3dpbmcgd2hhdCB3ZSd2ZSBzZWVuIG9mIHRoZSBkYXRhIGFscmVhZHksIHRoZSBpbmZvcm1hdGlvbiB3ZSdyZSBsb29raW5nIGZvciBpcyBpbiB0aGUgdGhpcmQgcm93LiBVc2luZyB0aGUgZ2VuZSBzeW1ib2xzIGluIG91ciBkYXRhLCB3ZSBjYW4NCg0KMS4gIE1hcCBvdXIgZ2VuZSBzeW1ib2xzIHRvIEdPIGFubm90YXRpb25zLCBhbmQNCjIuICBHYXRoZXIgdGhlIGdlbmUgbGVuZ3RocyBmb3Igb3VyIGRhdGEuDQoNClRoZSBzZWNvbmQgcG9ydGlvbiBvZiBpbmZvcm1hdGlvbiBpcyByZXF1aXJlZCBpbiB0aGUgZ2VuZXJhdGlvbiBvZiBhIHByb2JhYmlsaXR5IHdlaWdodGluZyBmdW5jdGlvbiAoUFdGKS4gVGhpcyBQV0Ygd2lsbCBkZXRlcm1pbmUgYSB3ZWlnaHRpbmcgZm9yIGVhY2ggZ2VuZSB3aGljaCBpcyBlc3NlbnRpYWxseSB0aGUgcHJvYmFiaWxpdHkgb2YgYSBnZW5lIGJlaW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiYXNlZCBvbiBpdHMgbGVuZ3RoIGFsb25lLiBJdCdzIGltcG9ydGFudCB0byBnZW5lcmF0ZSB3aXRoIGluZm9ybWF0aW9uIGFzIGl0IGNhbiBpbmZsdWVuY2Ugb3VyIGVucmljaG1lbnQgYW5hbHlzaXMuIFVsdGltYXRlbHkgd2UgdXNlIHRoZSBQV0YgdG8gaGVscCBpbmZvcm0gdGhlIGNyZWF0aW9uIG9mIGEgbnVsbCBkaXN0cmlidXRpb24gaW4gb3VyIGFuYWx5c2lzLg0KDQpXZSdsbCB1c2UgdGhlIGBudWxscCgpYCBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgUFdGIGZvciBvdXIgZGF0YXNldC4NCg0KYGBge3J9DQojIEdlbmVyYXRlIHRoZSBQV0YgZm9yIG91ciB2ZWN0b3Igb2YgZ2VuZXMNCnB3Zl9TQVJTX0NvVl8yIDwtIG51bGxwKERFZ2VuZXMgPSAuLi4sICAgICAgIyBPdXIgbGlzdCBvZiBERSBnZW5lcw0KICAgICAgICAgICAgICAgICAgICAgICAgZ2Vub21lID0gLi4uLCAgICAgICAgICAgICAgICAgIyBUaGUgR08gYW5ub3RhdGlvbiB3ZSB3YW50IHRvIHVzZQ0KICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAuLi4pICAgICAgICAgICAgICAgIyBUaGUgaWQgd2UnbGwgdXNlIHRvIGdlbmVyYXRlIHRoZSBkYXRhIHVzaW5nIGdlbmUgc3ltYm9scw0KDQpoZWFkKHB3Zl9TQVJTX0NvVl8yKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKkxvb2tpbmcgYXQgYSBwbG90IG9mIG91ciBQV0Y6KiogaGVyZSB3ZSBzZWUgYSBzdWJzZXQgb2YgZ2VuZXMgYW5kIHRoZWlyIFBXRiB2YWx1ZXMgdG8gc2VlIGhvdyB0aGV5IGZpdCBhY2NvcmRpbmcgdG8gb3VyIGxpbmUgb2YgYmVzdCBmaXQuIFRoZSBnb2FsIGlzIGZvciB0aGUgcG9pbnRzIHRvIGJlIHJlbGF0aXZlbHkgY2xvc2UgdG8gb3VyIGxpbmUgb2YgZml0LiBJZiB0aGUgdmFsdWVzIGFyZSBmYXIgb2ZmLCB0aGVyZSBtYXkgYmUgYW4gaXNzdWUgd2l0aCB0aGUgYW5ub3RhdGlvbiBkYXRhIG9yIGhvdyB5b3UgY2FsY3VsYXRlZCB5b3VyIGdlbmUgbGVuZ3RocyBmb3IgdGhlIFBXRi4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMS4zIEdlbmVyYXRlIGVucmljaGVkIEdPIHRlcm1zIGZyb20gb3VyIERFIGRhdGENCg0KV2UncmUgbm93IHJlYWR5IHRvIHF1ZXJ5IHRoZSBHTyBkYXRhYmFzZSBmb3IgdXAtIGFuZCBkb3duLXJlZ3VsYXRlZCB0ZXJtcyBpbiBvdXIgZGF0YS4gV2UnbGwgdXNlIHRoZSBgZ29zZXEoKWAgY29tbWFuZCB0byBkbyB0aGlzLCBwcm92aWRpbmcgb3VyIFBXRiBvYmplY3QgYXMgd2VsbCBhcyB0aGUgY29ycmVjdCBkYXRhYmFzZSBpbmZvcm1hdGlvbi4gVGhlIGRlZmF1bHQgbWV0aG9kIHVzZWQgaW4gdGhpcyBwcm9jZXNzIHRvIGdlbmVyYXRlIG91ciBlbnJpY2htZW50IGFuYWx5c2lzIGlzIHRoZSAqKipXYWxsZW5pdXMgYXBwcm94aW1hdGlvbioqKi4gWW91IGNhbiBjaG9vc2UgdG8gdXNlIHJhbmRvbSBzYW1wbGluZyBidXQgdGhpcyBtZXRob2QgY2FuIHRha2UgbXVjaCBsb25nZXIgd2l0aCBsaXR0bGUgZGlmZmVyZW5jZSBpbiByZXN1bHRzLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIHByb2Nlc3Mgc2VlIHRoZSBgZ29zZXFgIHBhY2thZ2UgdmlnbmV0dGUgW2hlcmVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9nb3NlcS9pbnN0L2RvYy9nb3NlcS5wZGYpDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBvdXIgZ28gZW5yaWNobWVudCBhbmFseXNpcw0KR08ud2FsbF9TQVJTX0NvVl8yIDwtIGdvc2VxKHB3ZiA9IC4uLiwgICAgICAjIFByb3ZpZGUgb3VyIHB3ZiBvYmplY3QNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vbWUgPSAiaGcxOSIsICAgICAgICAgICAjIFNldCB0aGUgYW5ub3RhdGlvbiBzZXQgdG8gdXNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAiZ2VuZVN5bWJvbCIsICAgICAgICAgIyBXaGljaCB2YXJpYWJsZSB3aWxsIHdlIHVzZSB0byBjb21wYXJlIHdpdGgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAuLi4gICAgICAgIyBIb3cgd2lsbCB5b3UgZGVjaWRlIG9uIHRoZSBudWxsIGRpc3RyaWJ1dGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQojIFdoYXQgZG9lcyB0aGUgZmluYWwgcmVzdWx0IGxvb2sgbGlrZT8NCmhlYWQoR08ud2FsbF9TQVJTX0NvVl8yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBXaGF0IGlzIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIHJlc3VsdGluZyBkYXRhPw0Kc3RyKEdPLndhbGxfU0FSU19Db1ZfMikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4yLjAgSW50ZXJwcmV0aW5nIG91ciBgZ29zZXFgIHJlc3VsdHMNCg0KTG9va2luZyBhdCB0aGUgcmVzdWx0cyBvZiBvdXIgYW5hbHlzaXMsIHdlIGFyZSByZXR1cm5lZCBhIGxpc3Rpbmcgb2YgMjIsNTAwIHVuaXF1ZSBHTyB0ZXJtcyBhY3Jvc3MgdGhlIDMgZG9tYWlucyAoQ0MsIEJQLCBhbmQgTUYpLiBXZSBhcmUgZ2l2ZW4gdHdvIHNldHMgb2YgcC12YWx1ZXMsIG9uZSBlYWNoIGZvciBvdmVyLXJlcHJlc2VudGVkIGFuZCB1bmRlci1yZXByZXNlbnRlZCB0ZXJtcy4gV2l0aCBlYWNoIHRlcm0gd2UgYWxzbyBzZWUgaG93IG1hbnkgdGltZXMgdGhhdCB0ZXJtIGlzIHJlcHJlc2VudGVkIGluIG91ciBERSBkYXRhIHZzIHRoZSB0b3RhbCBudW1iZXIgb2YgdGltZXMgdGhhdCB0ZXJtIG1heSBhcHBlYXIgaW4gdGhlIEdPIGRhdGFiYXNlIHdlIHF1ZXJpZWQuIEFzIHlvdSBjYW4gc2VlIGZyb20gdGhlIGZpcnN0IGZldyByb3dzIG9mIGRhdGEsIHNvbWUgR08gdGVybSBjYXRlZ29yaWVzIGNhbiBlbmNvbXBhc3MgYSBsYXJnZSBudW1iZXIgb2YgZ2VuZXMuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqUmVtZW1iZXIgaG93IHdlIGNyZWF0ZWQgdGhpcyBkYXRhISoqIExldCdzIGFsc28gcmVtaW5kIG91cnNlbHZlcyB0aGF0IHdlIHNldCB1cCBvdXIgREUgZ2VuZSBsaXN0IHVzaW5nIHRoZSBkYXRhIGZyb20gQTQ1OSBjZWxscyBpbmZlY3RlZCB3aXRoIFNBUlMtQ29WLTIuIExvb2tpbmcgYXQgdGhlIG1hbnVzY3JpcHQgaXRzZWxmLCB3aGlsZSBoYXZpbmcgYSBkaXN0aW5jdCBpbW11bmUgcmVzcG9uc2UsIHRoZSByZXBsaWNhdGlvbiBvZiB0aGUgdmlydXMgd2FzIHZlcnkgbG93IGluIHRoZXNlIGNlbGwgdHlwZXMgd2hpY2ggbGFjayB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgQUNFMiByZWNlcHRvci4gV2UncmUgcmVhbGx5IGp1c3QgdXNpbmcgdGhpcyBkYXRhIHRvIG1ha2Ugc29tZSB2aXN1YWxpemF0aW9ucyENCjo6Og0KDQpUbyBiZWdpbiBvdXIgYW5hbHlzaXMgd2UgY2FuIGZpbHRlciB0aGUgZW5yaWNoZWQgR08gdGVybXMgc2V0cyBieSBhcHBseWluZyBhIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdCBjb3JyZWN0aW9uIHdpdGggYHAuYWRqdXN0KClgIHVzaW5nIHRoZSBbQmVuamFtaW5pLUhvY2hiZXJnIG1ldGhvZCB0byBtaW5pbWl6ZSB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGVdKGh0dHBzOi8vd3d3LnB1YmxpY2hlYWx0aC5jb2x1bWJpYS5lZHUvcmVzZWFyY2gvcG9wdWxhdGlvbi1oZWFsdGgtbWV0aG9kcy9mYWxzZS1kaXNjb3ZlcnktcmF0ZSkuDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBhbiBlbnJpY2hlZCBHbyB0ZXJtIGRhdGFzZXQNCmVucmljaGVkLkdPX1NBUlNfQ29WXzIgPC0gDQojIEFkanVzdCB0aGUgcC12YWx1ZXMgb2YgdGhlIG92ZXItcmVwcmVzZW50ZWQgY29sdW1uDQojIEdPLndhbGxfU0FSU19Db1ZfMltwLmFkanVzdChHTy53YWxsX1NBUlNfQ29WXzIkb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUsIG1ldGhvZD0iQkgiKSA8IDAuMDUsIF0NCg0KR08ud2FsbF9TQVJTX0NvVl8yICU+JSANCmZpbHRlciguLi4pDQoNCiMgSG93IG1hbnkgdGVybXMgY29tZSBiYWNrIGZyb20gb3VyIGZpbHRlcmluZz8NCnN0cihlbnJpY2hlZC5HT19TQVJTX0NvVl8yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIG91ciBnb3NlcSBkYXRhIHRvIGxvb2sgYXQgYW5kIHZpc3VhbGl6ZQ0KIyBUaGlzIHdpbGwgYWxzbyBjb250YWluIHNvbWUgYWRkaXRpb25hbCBkYXRhc2V0cyB3ZSdsbCB3YW50IHRvIGxvb2sgYXQuDQpsb2FkKCIuL2RhdGEvTGVjdHVyZTA0LlJEYXRhIikNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBraW5kIG9mIHRlcm1zIGRvIHdlIHNlZSBpbiBvdXIgZW5yaWNobWVudD8NCmVucmljaGVkLkdPX1NBUlNfQ29WXzIkLi4uDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjIuMSBQbG90IG91ciBlbnJpY2hlZCBHTyB0ZXJtIGNhdGVnb3JpZXMgYXMgYSBkb3QgcGxvdA0KDQpMb29raW5nIGF0IG91ciBlbnJpY2hlZCBzZXQgb2YgR08gdGVybXMsIHdlIHNlZSB0ZXJtcyBsaWtlIGBpbmZsYW1tYXRvcnkgcmVzcG9uc2VgIGFuZCBgcmVzcG9uc2UgdG8gc3RyZXNzYC4gTm93IHRoYXQgd2UgaGF2ZSBhIHNldCBvZiBlbnJpY2hlZCBHTyB0ZXJtcywgd2UgY2FuIGZpbHRlciBhbmQgcGxvdCB0aGlzIGluZm9ybWF0aW9uIGFzIGEgZG90cGxvdCB3aXRoIHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyaXN0aWNzOg0KDQoxLiAgR08gdGVybXMgbGlzdGVkIGFsb25nIHRoZSB5LWF4aXMNCjIuICBzaXplIG9mIGRvdHMgcmVwcmVzZW50aW5nIGNhdGVnb3J5IHNpemUNCjMuICB1c2UgY29sb3VyIHRvIHJlcHJlc2VudCB0aGUgJSBvZiBERSBnZW5lcyBwcmVzZW50IGZyb20gdGhhdCB0aGF0IGNhdGVnb3J5DQoNCkxldCdzIGxvb2sgYXQgdGVybXMgdGhhdCBpbmNsdWRlIHRoZSB3b3JkICJyZXNwb25zZSIuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9DQoNCmVucmljaGVkLkdPX1NBUlNfQ29WXzIgJT4lIA0KICAjIEdlbmVyYXRlIGEgcGVyY2VudGFnZSB2YWx1ZSANCiAgbXV0YXRlKHBlcmNlbnRPZkNhdCA9IG51bURFSW5DYXQvbnVtSW5DYXQsDQogICAgICAgICAjIENyZWF0ZSBhIGR1bW15IGNhdGVnb3J5DQogICMgICAgICAgIHBhdGhvZ2VuID0gIlNBUlMtQ29WLTIiLA0KICAgICAgICAgIyBNYWtlIGEgc2V0IG9mIGFkanVzdGVkIHAtdmFsdWVzDQogICAgICAgICBGRFIgPSBwLmFkanVzdChvdmVyX3JlcHJlc2VudGVkX3B2YWx1ZSwgbWV0aG9kID0gIkJIIikNCiAgICAgICkgJT4lIA0KICANCiAgIyBGaWx0ZXIgZm9yIG9ubHkgdGVybXMgdGhhdCBpbmNsdWRlIHRoZSB3b3JkICJyZXNwb25zZSINCiAgZmlsdGVyKHN0cl9kZXRlY3QodGVybSwgInJlc3BvbnNlIikpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMjIyA0LjIuMSBzZXQgYWVzdGhldGljcyBieSBudW1ERUluQ2F0IGFuZCBGRFINCiAgICBhZXMoeD0iU0FSUy1Db1YtMiIsIHk9Li4uLCBzaXplPS4uLiwgY29sb3VyID0gLi4uKSArDQogIA0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogIA0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3NpemUocmFuZ2U9YygzLDEwKSkgKw0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2MoZGlyZWN0aW9uID0gLTEpICsgDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMi4yIFBsb3QgbXVsdGlwbGUgY29uZGl0aW9ucyB0byB0aGUgc2FtZSBkb3QgcGxvdA0KDQpXaXRoIGEgbGl0dGxlIGJpdCBvZiBlbGJvdyBncmVhc2UsIHdlIGNhbiBnZW5lcmF0ZSBhIGRhdGEgZnJhbWUgd2l0aCAqbXVsdGlwbGUqIEdPIGVucmljaG1lbnQgYW5hbHlzZXMgZm9yIG11bHRpcGxlIGV4cGVyaW1lbnRzIGFuZCBub3cgd2UgY2FuIGNvbXBhcmUgZW5yaWNobWVudCBhY3Jvc3MgZGF0YSBzZXRzISBPdXIgYExlY3R1cmUwNC5SRGF0YWAgY29udGFpbmVkIHRoZSBjb21iaW5lZCBleHBlcmltZW50YWwgR08gcmVzdWx0cyBpbiB0aGUgb2JqZWN0IGBlbnJpY2hlZC5HT19jb21iaW5lZC5kZmAuDQoNCmBgYHtyfQ0KIyBUYWtlIGEgbG9vayBhdCBvdXIgY29tYmluZWQgZGF0YSBmcmFtZQ0KZW5yaWNoZWQuR09fY29tYmluZWQuZGYNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQ0KIyBMb2FkIHVwIGEgY29tYmluZWQgZW5yaWNobWVudCBhbmFseXNpcw0KIyBsb2FkKCIuL2RhdGEvTGVjdHVyZTA0LlJEYXRhIikNCg0KZW5yaWNoZWQuR09fY29tYmluZWQuZGYgJT4lIA0KDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMjIyA0LjIuNCBTZXQgdGhlIG5ldyB4LWF4aXMgdmFsdWUgYW5kIGNvbG91ciBhZXN0aGV0aWMNCiAgICBhZXMoeD0uLi4sIHk9dGVybSwgc2l6ZT1udW1ERUluQ2F0LCBjb2xvdXIgPSAuLi4pICsNCiAgDQogICAgIyBUaGVtZQ0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCiAgDQogICAgIyAzLiBTY2FsaW5nDQogICAgc2NhbGVfc2l6ZShyYW5nZT1jKDMsMTApKSArDQogICAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSAtMSkgKyANCiAgDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fcG9pbnQoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjMuMCBVcHNldCBwbG90cw0KDQojIyMgNC4zLjEgQ2FuIFVwc2V0IHBsb3RzIGhlbHAgdXMgdG8gbWFrZSBzZW5zZSBvZiBvdmVybGFwcGluZyByZWxhdGlvbnNoaXBzPw0KDQpVcHNldCBwbG90cyBhcmUgYW4gYWx0ZXJuYXRpdmUgdG8gVmVubiBkaWFncmFtcyB0aGF0IHNob3cgdGhlIGludGVyc2VjdGlvbiBvZiBzZXRzLCBhcyB3ZWxsIGFzIHRoZSBzaXplIG9mIHRoZSBzZXRzLiBBZGRpdGlvbmFsbHksIFZlbm4gZGlhZ3JhbXMgY2FuIGJlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgZm9yIGdyZWF0ZXIgdGhhbiAyIG9yIDMgc2V0cy4gVGhpcyBpcyBhIHJlYWwgbGlmZSBmaWd1cmUgZnJvbSBCTUMgQmlvaW5mb3JtYXRpY3MuIFN1cmUgaXQgbG9va3MgcHJldHR5LCBidXQgd2hhdCBkb2VzIHRoZSBudW1iZXIgMjQgcmVwcmVzZW50IGluIHRoaXMgcGljdHVyZSBpbiB0ZXJtcyBvZiBBLCBCLCBDLCBELCBhbmQgRT8NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9GaWcyX0xhbV9CTUNCaW9pbmZvcm1hdGljczIwMTZfMTcucG5nP3Jhdz10cnVlIiB3aWR0aD0iNTAwIi8+DQoNCldoYXQgaXMgdGhlIG1lYW5pbmcgb2YgdGhlIHZhbHVlIDI0IGluIHRoaXMgZGlhZ3JhbT8gU3RhcmUgYXQgaXQgbG9uZyBlbm91Z2ggYW5kIHlvdSdsbCBzZWUgd2hpY2ggZ3JvdXAgaXQncyBpbiBidXQgaW1hZ2luZSB0aGlzIHdhcyAxMCBncm91cHM/DQo6OjoNCg0KVGhlICoqVXBTZXQgcGxvdCoqIHdhcyBmaXJzdCBwdWJsaXNoZWQgaW4gMjAxNCBhbmQgaGFzIGJlY29tZSBhIGhlbHBmdWwgdG9vbCBmb3IgdmlzdWFsaXppbmcgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIGNvbXBvbmVudHMgd2l0aCBkYXRhc2V0cy4gQWxvbmcgd2l0aCB0aGUgcHVibGljYXRpb24gY2FtZSBhIHBhY2thZ2UgZm9yIHdvcmtpbmcgd2l0aCB0aGVzZSBwbG90cy4gS25vd24gYXMgdGhlIFtgVXBTZXRSYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vaG1zLWRibWkvVXBTZXRSKSwgdGhlcmUgaGF2ZSBzaW5jZSBiZWVuIG1vcmUgcHJvamVjdHMgaW1wbGVtZW50aW5nIHRoaXMga2luZCBvZiB2aXN1YWxpemF0aW9uLiBXaGlsZSBgVXBTZXRSYCBoYXMgbm90IGJlZW4gdXBkYXRlZCBpbiBuZWFybHkgMyB5ZWFycywgYSBtb3JlIGFjdGl2ZSBwYWNrYWdlIGtub3duIGFzIFtgQ29tcGxleFVwc2V0YCBpcyBhdmFpbGFibGVdKGh0dHBzOi8va3Jhc3Nvd3NraS5naXRodWIuaW8vY29tcGxleC11cHNldC9pbmRleC5odG1sKS4NCg0KVG8gc29tZSBleHRlbnQsIHRoZSBgQ29tcGxleFVwc2V0YCBoYXMgZXh0ZW5zaWJpbGl0eSB3aXRoIGBnZ3Bsb3RgLCBhbGxvd2luZyB5b3UgdG8gdXNlIHNvbWV3aGF0IGZhbWlsaWFyIHN5bnRheCB0byBtb2RpZnkgdGhlc2UgcGxvdHMuIEFkZCB0byB0aGlzIHRoZSBhYmlsaXR5IHRvIHN0YWNrIG9yIGluY2x1ZGUgYWRkaXRpb25hbCB2aXN1YWxpemF0aW9ucyBvZiB5b3VyIGRhdGFzZXRzIGRpc3RyaWJ1dGlvbnMgb3Igb3RoZXIgY2hhcmFjdGVyaXN0aWNzLCBhbmQgdGhpcyBpcyBhIHByZXR0eSBhdHRyYWN0aXZlIHBhY2thZ2UgdG8gd29yayB3aXRoLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMy4yIFdvcmtpbmcgd2l0aCB0aGUgYENvbXBsZXhVcHNldGAgcGFja2FnZSB0byB2aXN1YWxpemUgb3ZlcmxhcHBpbmcgZGF0YXNldHMNCg0KTGV0J3Mgc2VlIGhvdyBVcFNldCBwbG90cyB3b3JrIHByYWN0aWNhbGx5LiBXZSBjYW4gY29tcGFyZSBvdXIgZGF0YSBmcm9tIGBibGFuY29fZGF0YV9sb25nLmRmYCBhbmQgY29tcGFyZSB0aGUgb3ZlcmxhcCBvZiBERSBnZW5lcyAoYWZ0ZXIgZmlsdGVyaW5nIG9yIG5vdCkuIFRvIGRvIHRoaXMgaW4gYSBwcmFjdGljYWwgc2Vuc2UsIHdlIGFnYWluIG5lZWQgdG8gY29udmVydCBvdXIgdmFsdWVzIHRvIGEgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBhZnRlciBzb21lIG1vcmUgZGF0YSB3cmFuZ2xpbmcuIE91ciBkYXRhIHdyYW5nbGluZyBzdGVwcyB3aWxsIGluY2x1ZGU6DQoNCjEuICBHZW5lcmF0aW5nIGEgc2hvcnQtbGlzdCBvZiBnZW5lcyB3aXRoIGEgaGlnaCBsb2d+Mn4gZm9sZC1jaGFuZ2UgYW5kIGxvdyBwLXZhbHVlLg0KMi4gIEZpbHRlcmluZyBvdXIgZGF0YSB0byBpbmNsdWRlIG9ubHkgdGhvc2UgZ2VuZXMuDQozLiAgQ29udmVydGluZyBvdXIgZGF0YSB0byBhIHdpZGUgZm9ybWF0IHdoZXJlIGVhY2ggY29sdW1uIHJlcHJlc2VudHMgdGhlIGluY2x1c2lvbiBzdGF0dXMgb2YgYSBnZW5lIChvYnNlcnZhdGlvbikgZm9yIGEgc3BlY2lmaWMgZXhwZXJpbWVudC4NCg0KTGV0J3MgbG9vayBhdCBvdXIgZGF0YSBzdHJ1Y3R1cmUgYWdhaW4uDQoNCmBgYHtyfQ0KaGVhZChibGFuY29fZGF0YV9sb25nLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpCZWdpbiBieSBtYWtpbmcgb3VyIHNob3J0LWxpc3Qgb2YgZ2VuZXMgdG8gZmlsdGVyIGJ5Lg0KDQpgYGB7cn0NCiMgZmlsdGVyIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyBpbiBvdXIgU0FSUy1Db1YtMi9BNTQ5IGRhdGEgc2V0IGFuZCBtYWtlIGEgbGlzdCBvZiBnZW5lIG5hbWVzDQpTQVJTX0NvVl8yLmxpc3QgPC0NCiAgYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQogIA0KICAjIEZpbHRlciBvdXIgZGF0YQ0KICBmaWx0ZXIocGF0aG9nZW4gPT0gIlNBUlMtQ29WLTIiLCANCiAgICAgICAgIGhvc3QgPT0gIkE1NDkiLCANCiAgICAgICAgLi4uKSAlPiUgDQogIA0KICAjIFNlbGVjdCBqdXN0IG91ciBnZW5lIG5hbWVzIHVzaW5nIGEgc2hvcnRoYW5kIGNvbnZlcnNpb24NCiAgcHVsbChHZW5lTmFtZSkNCiAgDQogICMgTGVzcyBjb2RlIHRoYW4gdGhlIGZvbGxvd2luZyBzdGVwczoNCiAgIyBkcGx5cjo6c2VsZWN0KEdlbmVOYW1lKSAlPiUgdW5saXN0KCkgJT4lIGFzLmNoYXJhY3RlcigpDQoNClNBUlNfQ29WXzIubGlzdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpGaWx0ZXIgb3VyIGBibGFuY29fZGF0YV9sb25nLmRmYCBhbmQgcGl2b3QgdG8gYSB3aWRlLWZvcm1hdCBjb250YWluaW5nIG91ciBsb2dpY2FsIHZhbHVlcyBmb3IgaW5jbHVzaW9uLg0KDQpgYGB7cn0NCiMgR2VuZXJhdGUgb3VyIHVwc2V0IGRhdGENCmJsYW5jb191cHNldC5kZiA8LQ0KICBibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgDQogICMgRmlsdGVyIG91ciBkYXRhIGxpc3QgdXNpbmcgb3VyIGdlbmVzIG9mIGludGVyZXN0DQogIGZpbHRlcihHZW5lTmFtZSAlaW4lIC4uLikgJT4lIA0KICANCiAgIyBjb252ZXJ0IG91ciBMMkZDIGRhdGEgdG8gYSBsb2dpY2FsIGJhc2VkIG9uIGEgdmFsdWUgb2YgPjINCiAgbXV0YXRlKHVwcmVndWxhdGVkID0gLi4uKSAlPiUgDQogIA0KICAjIFNlbGVjdCBqdXN0IHRoZSBnZW5lIG5hbWVzLCBleHBlcmltZW50cywgYW5kIHRoZSBsb2dpY2FsIHZhcmlhYmxlDQogIGRwbHlyOjpzZWxlY3QoR2VuZU5hbWUsIGV4cGVyaW1lbnQsIC4uLikgJT4lIA0KICANCiAgIyBQaXZvdCBvdXIgZGF0YSB3aWRlIHNvIHRoYXQgZWFjaCBHZW5lTmFtZSBpcyBub3cgYSByb3csIGVhY2ggZXhwZXJpbWVudCBpcyBhIGNvbHVtbg0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gLi4uLCB2YWx1ZXNfZnJvbSA9IC4uLikgJT4lIA0KICANCiAgIyBDb252ZXJ0IHRvIGEgZGF0YWZyYW1lIGZvciB0aGUgQ29tcGxleFVwc2V0IHBhY2thZ2UgKG9yIGl0IHdpbGwgdGhyb3cgYW4gZXJyb3IpDQogIGFzLmRhdGEuZnJhbWUoKQ0KICANCnN0cihibGFuY29fdXBzZXQuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjMuMyBHZW5lcmF0aW5nIG91ciB1cHNldCBkYXRhDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gb3VyIGRhdGEgdHJhbnNmb3JtYXRpb25zLCB3ZSBub3cgaGF2ZSBhbGwgb2Ygb3VyIGV4cGVyaW1lbnRzIGxpc3RlZCBpbiBjb2x1bW5zLCB3aXRoIGVhY2ggZ2VuZSByZXByZXNlbnRlZCBpbiBlYWNoIHJvdy4gQXMgd2UgbG9vayBkb3duIGVhY2ggY29sdW1uIHdlIHNlZSBhIHZhbHVlIG9mIGBUUlVFYCBvciBgRkFMU0VgIHVzZWQgdG8gZGlmZmVyZW50aWF0ZSBpZiB0aGVyZSB3YXMgb3ZlcmV4cHJlc3Npb24gb2YgdGhhdCBnZW5lIGluIHRoYXQgc3BlY2lmaWMgZXhwZXJpbWVudGFsIGNvbnRleHQuDQoNCklmIHdlIGhhZCBub3QgZmlsdGVyZWQgYmFzZWQgb24gc29tZSBzZXQgb2YgZ2VuZXMsIHdlIHdvdWxkIGhhdmUgYSBkYXRhIGZyYW1lIHdpdGggbW9yZSB0aGFuIDIzSyBjb2x1bW5zISBOb3cgdGhhdCB3ZSBoYXZlIG91ciBnZW5lcyBwcmVzZW50ZWQgaW4gdGhpcyB3YXkgd2UgY2FuIHByb2NlZWQgdG8gZ2VuZXJhdGUgYW4gdXBzZXQgcGxvdC4NCg0KV29ya2luZyB3aXRoIHRoZSBgdXBzZXQoKWAgZnVuY3Rpb24gdG8gYnVpbGQgb3VyIHBsb3QsIHdlIHdpbGwgY29uY2VybiBvdXJzZWx2ZXMgd2l0aCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgZGF0YWA6IGEgZGF0YWZyYW1lIGluY2x1ZGluZyBiaW5hcnkgY29sdW1ucyB0aGF0IHJlcHJlc2VudCBtZW1iZXJzaGlwIGluIGVhY2ggY2xhc3MuDQotICAgYGludGVyc2VjdGA6IGEgbGlzdCBvZiBjb2x1bW4gbmFtZXMgd2hpY2ggd2lsbCBiZSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBpbnRlcnNlY3RpbmcgZGF0YS4NCi0gICBgbmFtZWA6IHRoZSBsYWJlbCBzaG93biBiZWxvdyB0aGUgaW50ZXJzZWN0aW9uIG1hdHJpeC4NCi0gICBgaGVpZ2h0X3JhdGlvYDogcmF0aW8gb2YgdGhlIGludGVyc2VjdGlvbiBtYXRyaXggdG8gdGhlIGludGVyc2VjdGlvbiBoZWlnaHQgKGRlZmF1bHQgPSAwLjUpLg0KLSAgIGB3aWR0aF9yYXRpb2A6IHJhdGlvIG9mIHRoZSBvdmVyYWxsIHNldCBzaXplIHdpZHRoIHRvIGludGVyc2VjdGlvbiBtYXRyaXggd2lkdGggKGRlZmF1bHQgPSAwLjMpLg0KLSAgIGBtaW5fc2l6ZWAgYW5kIGBtYXhfc2l6ZWA6IG1pbmltYWwgYW5kIG1heGltYWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgYW4gaW50ZXJzZWN0aW9uIHRvIGJlIGluY2x1ZGVkLg0KLSAgIGBtaW5fZGVncmVlYCBhbmQgYG1heF9kZWdyZWVgOiBtaW5pbWFsIGFuZCBtYXhpbWFsIG51bWJlciBvZiBkZWdyZWVzIGZvciBhbiBpbnRlcnNlY3Rpb24gdG8gYmUgaW5jbHVkZWQuDQotICAgYG5faW50ZXJzZWN0aW9uc2A6IHRoZSBtYXhpbXVtIG51bWJlciBvZiBpbnRlcnNlY3Rpb25zIHRvIGRpc3BsYXkgYmFzZWQgb24gb3VyIGBtaW5fKmAgb3IgYG1heF8qYCBjcml0ZXJpYS4NCi0gICBgdGhlbWVzYDogYSBwYXJhbWV0ZXIgdXNlZCB0byBwYXNzIHRoZW1lIGNoYW5nZXMgdGhyb3VnaCB0aGUgYHVwc2V0X2RlZmF1bHRfdGhlbWVzKClgIG9yIGB1cHNldF9tb2RpZnlfdGhlbWVzKClgIGZ1bmN0aW9ucy4NCg0KYGBge3IsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xMH0NCg0KIyBHZW5lcmF0ZSBvdXIgdXBzZXQgcGxvdA0KDQp1cHNldChkYXRhID0gLi4uLCAgICAgICAgICAgICAgICAgICAgICAgIyBQcm92aWRlIG91ciBkYXRhc2V0DQogICAgICBpbnRlcnNlY3QgPSAuLi4sICAjIE5hbWUgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBhbmFseXNlIA0KICAgICAgbmFtZT0nRXhwZXJpbWVudGFsIGNvbmRpdGlvbicsICAgICAgICAgICAgICAgICMgU2V0IHRoZSBsYWJlbCBiZWxvdyB0aGUgaW50ZXJzZWN0aW9uIG1hdHJpeA0KICAgICAgd2lkdGhfcmF0aW89MC4xLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSB0aGUgU2V0IHNpemUgd2lkdGggYSBsaXR0bGUgc21hbGxlcg0KICAgICAgbWluX3NpemUgPSAuLi4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZXF1aXJlIHRoZXJlIHRvIGJlIGEgbWluaW11bSBvZiAyIG1lbWJlcnMgdG8gc2hvdyBhbiBpbnRlcnNlY3Rpb24NCiAgICAgIG5faW50ZXJzZWN0aW9ucyA9IC4uLiwgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgdGhlIG1heCBudW1iZXIgb2YgaW50ZXJzZWN0aW9ucyB3ZSB3YW50IHRvIHBsb3QNCiAgICAgIHRoZW1lcyA9IHVwc2V0X2RlZmF1bHRfdGhlbWVzKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgIyBTZXQgdGhlIHBsb3QgdGV4dCBzaXplIHRvIDIwDQogICAgICkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMy40IEludGVycHJldGluZyBvdXIgVXBzZXQgcGxvdA0KDQpGcm9tIG91ciB1cHNldCBwbG90LCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSAzIHJlZ2lvbnMuDQoNCjEuICBUaGUgbGVmdC1oYW5kIGJhcnBsb3QgZGVub3RlcyB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIHNldC9jYXRlZ29yeS4gSW4gdGhpcyBjYXNlIHdlJ3JlIHRhbGtpbmcgYWJvdXQgZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMuDQoNCjIuICBUaGUgYm90dG9tIHBsb3QgZ3JhcGhpY2FsbHkgcmVwcmVzZW50cyB0aGUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBlYWNoIGNhdGVnb3J5Lg0KDQozLiAgVGhlIHVwcGVyIGJhcnBsb3QgZGlzcGxheXMgdGhlIG51bWJlciBvZiBvY2N1cmVuY2VzIGZvciB0aGUgY29tYmluYXRpb24gZGlzcGxheWVkIGluIHRoZSBib3R0b20gcGxvdC4NCg0KRnJvbSBvdXIgc2V0IHNpemVzLCBpdCBsb29rcyBsaWtlIGEgTDJGQyB2YWx1ZSBvZiBcPiAyIHdhcyBwZXJoYXBzIHRvbyBzdHJpbmdlbnQgZm9yIHRoZSBJQVYgYW5kIE1FUlMtQ29WIERFIGRhdGEuIFNpbmNlIHdlIGRpZCBmaWx0ZXIgb3VyIGdlbmVzIGluaXRpYWxseSBieSB0aGUgYmVzdCBoaXRzIGluIG91ciBTQVJTLUNPVi0yKEE1NDkpIGRhdGFzZXQsIHdlIGNhbiBzZWUgdGhhdCBpdCBoYXMgdGhlIGhpZ2hlc3QgZnJlcXVlbmN5IGdyb3VwIGF0IDM0IGdlbmVzIHdpdGggb3ZlciBleHByZXNzaW9uIGluIGp1c3QgdGhpcyBzZXQuIFRoZSBuZXh0IGxhcmdlc3Qgb3ZlcmxhcHBpbmcgc2V0IGlzIG9mIDE0IGdlbmVzIGJldHdlZW4gdGhlIFNBUlMtQ29WLTIoQTU0OSkgc2V0IGFuZCB0aGUgUlNWKEE1NDkpIERFIGRhdGEuDQoNCllvdSBjYW4gYWxzbyBzZWUgZnJvbSB0aGUgcGxvdCB0aGF0IHRoZXJlIGFyZSBvbmx5IDkgY29tYmluYXRpb25zIG9mIGRhdGFzZXRzIHdpdGggYW55IHJlYWwgb3ZlcmxhcC4gSGFkIHdlIHNldCBgbWluX3NpemUgPSAxYCBpbnN0ZWFkLCB0aGUgcmVtYWluZGVyIG9mIHRoZSBncm91cGluZyBjb21iaW5hdGlvbnMgd291bGQgYmUgc2VlbiB0byBjb250YWluIGp1c3QgYSBzaW5nbGUgZ2VuZSBpbiBlYWNoLg0KDQpPdmVyYWxsIHRoaXMgY2FuIG1ha2UgZm9yIGEgc2ltcGxpZmllZCBpbnRlcnByZXRhdGlvbiBvZiBvdmVybGFwcGluZyBnZW5lcyB2ZXJzdXMgbW9yZSBjb21wbGljYXRlZCBWZW5uIGRpYWdyYW1zIQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LjAuMCBDbGFzcyBzdW1tYXJ5DQoNClRvZGF5IHdlIHRvb2sgYSBsb25nIGxvb2sgYXQgc29tZSBwb3B1bGFyIHZpc3VhbGl6YXRpb25zIGZvciBleHByZXNzaW9uIGRhdGEgc2V0cyBnZW5lcmF0ZWQgYnkgYW4gZXhwZXJpbWVudCBsaWtlIFJOQXNlcS4gT3VyIGFuYWx5c2VzIGZvY3VzZWQgbW9yZSBvbiBoaWdobGlnaHRpbmcgYXNwZWN0cyBvZiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gaXRzZWxmIGZyb20gYSBiaWctcGljdHVyZSBsb3ctcmVzb2x1dGlvbiB2aWV3IGxpa2Ugdm9sY2FubyBwbG90cyB0byB6b29taW5nIGRvd24gdG8gdGhlIGluZGl2aWR1YWwgZ2VuZSBsZXZlbCB3aXRoIGRvdCBwbG90cy4NCg0KTmV4dCB3ZWVrIHdlJ2xsIGxvb2sgYXQgZXhhbXBsZXMgb2YgImJpZyIgZGF0YSBmcm9tIGFuIGV2ZW4gYnJvYWRlciBzZW5zZSBieSB1c2luZyBkaW1lbnNpb25hbCByZWR1Y3Rpb24gdGVjaG5pcXVlcyBsaWtlIGNsYXNzaWZ5aW5nIG91ciBkYXRhc2V0cyBpbnRvIGdyb3VwcyB3aXRoIHByaW5jaXBsZSBjb21wb25lbnQgYW5hbHlzaXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjEuMCBXZWVrbHkgYXNzaWdubWVudA0KDQpUaGlzIHdlZWsncyBhc3NpZ25tZW50IHdpbGwgYmUgZm91bmQgdW5kZXIgdGhlIGN1cnJlbnQgbGVjdHVyZSBmb2xkZXIgdW5kZXIgdGhlICJhc3NpZ25tZW50IiBzdWJmb2xkZXIuIEl0IHdpbGwgaW5jbHVkZSBhbiBSIG1hcmtkb3duIG5vdGVib29rIHRoYXQgeW91IHdpbGwgdXNlIHRvIHByb2R1Y2UgdGhlIGNvZGUgYW5kIGFuc3dlcnMgZm9yIHRoaXMgd2VlaydzIGFzc2lnbm1lbnQuIFBsZWFzZSBwcm92aWRlIGFuc3dlcnMgaW4gbWFya2Rvd24gb3IgY29kZSBjZWxscyB0aGF0IGltbWVkaWF0ZWx5IGZvbGxvdyBlYWNoIHF1ZXN0aW9uIHNlY3Rpb24uDQoNCnwgICAgICAgICAgICAgICAgICAgIHwgQXNzaWdubWVudCBicmVha2Rvd24gfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICAgICAgICBDb2RlICAgICAgICB8ICAgICAgICAgNTAlICAgICAgICAgIHwgXC0gRG9lcyBpdCBmb2xsb3cgYmVzdCBwcmFjdGljZXM/ICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIERvZXMgaXQgbWFrZSBnb29kIHVzZSBvZiBhdmFpbGFibGUgcGFja2FnZXM/IHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBXYXMgZGF0YSBwcmVwYXJlZCBwcm9wZXJseSAgICAgICAgICAgICAgICAgICB8DQp8IEFuc3dlcnMgYW5kIE91dHB1dCB8ICAgICAgICAgNTAlICAgICAgICAgIHwgXC0gSXMgb3V0cHV0IGJhc2VkIG9uIHRoZSBjb3JyZWN0IGRhdGFzZXQ/ICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBncm91cGluZ3MgYXBwcm9wcmlhdGUgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBBcmUgY29ycmVjdCB0aXRsZXMvYXhlcy9sZWdlbmRzIGNvcnJlY3Q/ICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gSXMgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGdyYXBocyBjb3JyZWN0PyAgICAgfA0KDQpTaW5jZSBjb2Rpbmcgc3R5bGVzIGFuZCBzb2x1dGlvbnMgY2FuIGRpZmZlciwgc3R1ZGVudHMgYXJlIGVuY291cmFnZWQgdG8gdXNlIGJlc3QgcHJhY3RpY2VzLiBBc3NpZ25tZW50cyAqbWF5KiBiZSByZXdhcmRlZCBmb3Igd2VsbC1jb2RlZCBvciBlbGVnYW50IHNvbHV0aW9ucy4NCg0KWW91IGNhbiBzYXZlIGFuZCBkb3dubG9hZCB0aGUgbWFya2Rvd24gbm90ZWJvb2sgaW4gaXRzIG5hdGl2ZSBmb3JtYXQuIFN1Ym1pdCB0aGlzIGZpbGUgdG8gdGhlIHRoZSBhcHByb3ByaWF0ZSBhc3NpZ25tZW50IHNlY3Rpb24gYnkgMTI6NTkgcG0gb24gdGhlIGRhdGUgb2Ygb3VyIG5leHQgY2xhc3M6IEFwcmlsIDExdGgsIDIwMjQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjIuMCBBY2tub3dsZWRnZW1lbnRzDQoNCioqUmV2aXNpb24gMS4wLjAqKjogY3JlYXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMUggUyBMRUMwMTQxKiosIDAzLTIwMjEgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMSoqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIyIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMS4wLjIqKjogZWRpdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyMyBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDIuMC4wKio6IFJldmlzZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDI0IGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjMuMCBSZWZlcmVuY2VzDQoNClNhbmtleSBkaWFncmFtIGRvY3VtZW50YXRpb246IDxodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbmV0d29ya0QzL3ZlcnNpb25zLzAuNC90b3BpY3Mvc2Fua2V5TmV0d29yaz4NCg0KYHJpdmVycGxvdGAsIGFub3RoZXIgcGFja2FnZSBmb3IgbWFraW5nIHlvdXIgU2Fua2V5IGRpYWdyYW1zOiA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JpdmVycGxvdC9yaXZlcnBsb3QucGRmPg0KDQpNQSBwbG90cyBkaXJlY3RseSBmcm9tIHR3byBzZXRzIG9mIGV4cHJlc3Npb24gZGF0YTogPGh0dHBzOi8vcnBrZ3MuZGF0YW5vdmlhLmNvbS9nZ3B1YnIvcmVmZXJlbmNlL2dnbWFwbG90Lmh0bWw+DQoNCkdlbmVyYXRpbmcgYGdvc2VxYCBkYXRhIGZvciBSTkEgYW5hbHlzaXM6IDxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvZ29zZXEvaW5zdC9kb2MvZ29zZXEucGRmPg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGhlIENlbnRlciBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikNCg0KVGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikgYXQgdGhlIFVuaXZlcnNpdHkgb2YgVG9yb250byBvZmZlcnMgY29tcHJlaGVuc2l2ZSBleHBlcmltZW50YWwgZGVzaWduLCByZXNlYXJjaCwgYW5kIGFuYWx5c2lzIHNlcnZpY2VzIGluIG1pY3JvYmlvbWUgYW5kIG1ldGFnZW5vbWljIHN0dWRpZXMsIGdlbm9taWNzLCBwcm90ZW9taWNzLCBhbmQgYmlvaW5mb3JtYXRpY3MuDQoNCkZyb20gdGFyZ2V0ZWQgRE5BIGFtcGxpY29uIHNlcXVlbmNpbmcgdG8gdHJhbnNjcmlwdG9tZXMsIHdob2xlIGdlbm9tZXMsIGFuZCBtZXRhZ2Vub21lcywgZnJvbSBwcm90ZWluIGlkZW50aWZpY2F0aW9uIHRvIHBvc3QtdHJhbnNsYXRpb25hbCBtb2RpZmljYXRpb24sIENBR0VGIGhhcyB0aGUgdG9vbHMgYW5kIGtub3dsZWRnZSB0byBzdXBwb3J0IHlvdXIgcmVzZWFyY2guIE91ciBzdGF0ZS1vZi10aGUtYXJ0IGZhY2lsaXR5IGFuZCBleHBlcmllbmNlZCByZXNlYXJjaCBzdGFmZiBwcm92aWRlIGEgYnJvYWQgcmFuZ2Ugb2Ygc2VydmljZXMsIGluY2x1ZGluZyBib3RoIHN0YW5kYXJkIGFuYWx5c2VzIGFuZCB0ZWNobmlxdWVzIGRldmVsb3BlZCBieSBvdXIgdGVhbS4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSBzcGVjaWFsIGV4cGVydGlzZSBpbiBtaWNyb2JpYWwsIHBsYW50LCBhbmQgZW52aXJvbm1lbnRhbCBzeXN0ZW1zLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1cyBhbmQgdGhlIHNlcnZpY2VzIHdlIG9mZmVyLCBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vd3d3LmNhZ2VmLnV0b3JvbnRvLmNhLz4uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfbmV3LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQo=